diff --git a/.gitignore b/.gitignore index 71c9194e8bd..90fdd61a111 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ src/test/data/sandbox/ # MacOS custom attributes files created by Finder .DS_Store docs/_site/ + +*.class +bin/ diff --git a/README.md b/README.md index 13f5c77403f..f7118ebc86e 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,23 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) ![Ui](docs/images/Ui.png) -* This is **a sample project for Software Engineering (SE) students**.
- Example usages: - * as a starting point of a course project (as opposed to writing everything from scratch) - * as a case study -* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details. - * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big. +# Trackr + +[![CI Status](https://github.com/AY2223S2-CS2103T-W14-2/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2223S2-CS2103T-W14-2/tp/actions) +[![codecov](https://codecov.io/gh/AY2223S2-CS2103T-W15-2/tp/branch/master/graph/badge.svg?token=DBXVG742QT)](https://codecov.io/gh/AY2223S2-CS2103T-W15-2/tp) + +This repository contains the code base for a Java application for small online bakery businesses. + +## About The Project + +* The project is a desktop application (called _Trackr_) that targets users with a Command Line Interface (CLI), while providing the visuals using a Graphical User Interface (GUI). + * It is a software engineering project for a module (**CS2103T Software Engineering**) by National University of Singapore (NUS). + * It is **written in OOP fashion**. * It comes with a **reasonable level of user and developer documentation**. -* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...). -* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**. -* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info. +* It is named `Trackr` because it was created to help users **track** their orders, suppliers contact information and tasks. +* For the detailed documentation of this project, see the **[Trackr Product Website](https://ay2223s2-cs2103t-w15-2.github.io/tp/)**. + +## Acknowledgements + +* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). +* If you would like to contribute code to the parent project (AddressBook-Level3), see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info. diff --git a/build.gradle b/build.gradle index 108397716bd..944376bccc6 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { id 'jacoco' } -mainClassName = 'seedu.address.Main' +mainClassName = 'trackr.Main' sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 @@ -66,7 +66,11 @@ dependencies { } shadowJar { - archiveFileName = 'addressbook.jar' + archiveFileName = 'trackr.jar' +} + +run { + enableAssertions = true } defaultTasks 'clean', 'test' diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..60ad8411186 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -9,51 +9,52 @@ You can reach us at the email `seer[at]comp.nus.edu.sg` ## Project team -### John Doe +### Liu Muchen - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/LiuMC-SG)] +[[portfolio](team/liumc-sg.md)] -* Role: Project Advisor +* Role: Software Developer +* Responsibilities: Team Lead (Documentation, Deliverables) -### Jane Doe +### Darren Chang - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/ChangGittyHub)] +[[portfolio](team/changgittyhub.md)] -* Role: Team Lead -* Responsibilities: UI +* Role: Software Developer +* Responsibilities: Scheduling & Tracking -### Johnny Doe +### Arkar Kyaw Aung - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](https://github.com/arkarsg)] +[[portfolio](team/arkarsg.md)] * Role: Developer -* Responsibilities: Data +* Responsibilities: UI & Testing -### Jean Doe +### Hmuu Myat Moe - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/HmuuMyatMoe)] +[[portfolio](team/hmuumyatmoe.md)] * Role: Developer -* Responsibilities: Dev Ops + Threading +* Responsibilities: Documentation & Code Quality -### James Doe +### Chong Wei Guan - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/chongweiguan)] +[[portfolio](team/chongweiguan.md)] * Role: Developer -* Responsibilities: UI +* Responsibilities: Deliverables & Deadlines diff --git a/docs/DevOps.md b/docs/DevOps.md index d2fd91a6001..c8033acae4a 100644 --- a/docs/DevOps.md +++ b/docs/DevOps.md @@ -73,7 +73,7 @@ Any warnings or errors will be printed out to the console. Here are the steps to create a new release. -1. Update the version number in [`MainApp.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java). +1. Update the version number in [`MainApp.java`](https://github.com/AY2223S2-CS2103T-W15-2/tp/blob/master/src/main/java/trackr/MainApp.java). 1. Generate a fat JAR file using Gradle (i.e., `gradlew shadowJar`). 1. Tag the repo with the version number. e.g. `v0.1` 1. [Create a new release using GitHub](https://help.github.com/articles/creating-releases/). Upload the JAR file you created. diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..75659575557 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -2,246 +2,626 @@ layout: page title: Developer Guide --- -* Table of Contents -{:toc} --------------------------------------------------------------------------------------------------------------------- +# Overview + +Trackr is a desktop application catered towards home businesses to track their _suppliers_, _customers_, _orders_, _menu items_ and _tasks_. It is designed for users who are quick typers to accomplish their tasks through the _Command Line Interface (CLI)_ while reaping the benefits of a _Graphical User Interface (GUI)_. + +
+ +:bulb: **Tip** -## **Acknowledgements** +Texts that are in _italics_ are further explained in the [Glossary section](#46-glossary). -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +
+ +# About This Guide + +This guide is intended for developers who want to work on **Trackr** project, where the code base is available [here](https://github.com/AY2223S2-CS2103T-W15-2/tp). + +It explains the different components within the project and how they interact with each other. + +This is a guide for developers looking to contribute to the codebase. There are explanations as to how the project is setup and how each command works. + +You can click on the respective links below to read up on the relevant sections. + +# Table of Contents + + +* [1 Introduction](#1-introduction) + * [1.1 Setting up, getting started](#11-setting-up-getting-started) + * [1.2 About Us](#12-about-us) + * [1.3 Acknowledgements](#13-acknowledgements) +* [2 Design](#2-design) + * [2.1 Architecture](#21-architecture) + * [2.1.1 Main components of the architecture](#211-main-components-of-the-architecture) + * [2.1.2 How the architecture components interact with each other](#212-how-the-architecture-components-interact-with-each-other) + * [2.2 UI component](#22-ui-component) + * [2.3 Logic component](#23-logic-component) + * [2.4 Model component](#24-model-component) + * [2.4.1 Item](#241-item) + * [2.4.2 Supplier & Customer](#242-supplier--customer) + * [2.4.3 Task](#243-task) + * [2.4.4 Menu](#244-menu) + * [2.4.5 Order](#245-order) + * [2.5 Storage component](#25-storage-component) + * [2.6 Common classes](#26-common-classes) +* [3 Implementation](#3-implementation) + * [3.1 AddXYZCommand](#31-addxyzcommand) + * [3.2 DeleteXYZCommand](#32-deletexyzcommand) + * [3.3 EditXYZCommand](#33-editxyzcommand) + * [3.4 FindXYZCommand](#34-findxyzcommand) + * [3.5 ListXYZCommand](#35-listxyzcommand) + * [3.6 SortXYZCommand](#36-sortxyzcommand) + * [3.7 ClearXYZCommand](#37-clearxyzcommand) + * [3.8 TabCommand](#38-tabcommand) + * [3.9 HelpCommand](#39-helpcommand) + * [3.10 ExitCommand](#310-exitcommand) +* [4 Appendix](#4-appendix) + * [4.1 Documentation, logging, testing, configuration, dev-ops](#41-documentation-logging-testing-configuration-dev-ops) + * [4.2 Product scope](#42-product-scope) + * [4.3 User stories](#43-user-stories) + * [4.4 Use cases](#44-use-cases) + * [4.5 Non-Functional Requirements](#45-non-functional-requirements) + * [4.6 Glossary](#46-glossary) + * [4.7 Planned Enhancements](#47-planned-enhancements) + * [4.8 Instructions for manual testing](#48-instructions-for-manual-testing) + * [Launch and shutdown](#launch-and-shutdown) + * [Adding a supplier](#adding-a-supplier) + * [Adding a task](#adding-a-task) + * [Adding an order](#adding-an-order) + * [Adding a menu item](#adding-a-menu-item) + * [Editing supplier](#editing-supplier) + * [Editing task](#editing-task) + * [Editing order](#editing-order) + * [Editing menu item](#editing-menu-item) + * [Find supplier](#find-supplier) + * [Find task](#find-task) + * [Find order](#find-order) + * [Find menu item](#find-menu-item) + * [Delete supplier](#delete-supplier) + * [Delete task](#delete-task) + * [Delete order](#delete-order) + * [Delete menu item](#delete-menu-item) + * [Sort task](#sort-task) + * [Sort order](#sort-order) + * [List all suppliers](#list-all-suppliers) + * [List all tasks](#list-all-tasks) + * [List all orders](#list-all-orders) + * [List all menu items](#list-all-menu-items) + * [Clear all suppliers](#clear-all-suppliers) + * [Clear all tasks](#clear-all-tasks) + * [Clear all orders](#clear-all-orders) + * [Clear all menu items](#clear-all-menu-items) + * [Switch tabs](#switch-tabs) + -------------------------------------------------------------------------------------------------------------------- -## **Setting up, getting started** +# 1 Introduction + +## 1.1 Setting up, getting started + +Refer to the guide [here](SettingUp.md) on how to set up your own version of our project. -Refer to the guide [_Setting up and getting started_](SettingUp.md). +## 1.2 About Us + +For more information about our team, you can refer to this webpage [here](AboutUs.md). + +## 1.3 Acknowledgements + +* This project is based on the AddressBook-Level3 (AB3) project created by the [SE-EDU initiative](https://se-education.org). +* If you would like to contribute code to the parent project (AddressBook-Level3), see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info.

-------------------------------------------------------------------------------------------------------------------- -## **Design** +# 2 Design + +This section will show our design methodology on the different components and how they interact with each other.
-: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/AY2223S2-CS2103T-W15-2/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 +## 2.1 Architecture - +

+ +
Figure 1: Architecture Class Diagram +

The ***Architecture Diagram*** given above explains the high-level design of the App. Given below is a quick overview of main components and how they interact with each other. -**Main components of the architecture** +### 2.1.1 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, -* 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. +**`Main`** has two classes called [`Main`](https://github.com/AY2223S2-CS2103T-W15-2/tp/blob/master/src/main/java/trackr/Main.java) and [`MainApp`](https://github.com/AY2223S2-CS2103T-W15-2/tp/blob/master/src/main/java/trackr/MainApp.java). It is responsible for, -[**`Commons`**](#common-classes) represents a collection of classes used by multiple other components. +* At app launch: Initializing the components in the correct sequence, and connecting them up with each other. +* At shut down: Shutting down the components and invoking cleanup methods where necessary. -The rest of the App consists of four components. +[**`Commons`**](#26-common-classes) represents a collection of classes used by multiple other components. -* [**`UI`**](#ui-component): The UI of the App. -* [**`Logic`**](#logic-component): The command executor. -* [**`Model`**](#model-component): Holds the data of the App in memory. -* [**`Storage`**](#storage-component): Reads data from, and writes data to, the hard disk. +The rest of the App consists of four components. +* [**`UI`**](#22-ui-component): The UI of the App. +* [**`Logic`**](#23-logic-component): The command executor. +* [**`Model`**](#24-model-component): Holds the data of the App in memory. +* [**`Storage`**](#25-storage-component): Reads data from, and writes data to, the hard disk. -**How the architecture components interact with each other** +### 2.1.2 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 `delete_supplier 1`. - +

+ +
Figure 2: Sequence Diagram (Deleting Person) +

Each of the four main components (also shown in the diagram above), -* defines its *API* in an `interface` with the same name as the Component. -* implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point. +* defines its API in an `interface` with the same name as the Component. +* implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point). -For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below. +For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (Reason: to prevent components outside from being coupled to the implementation of a component), as illustrated in the (partial) class diagram below. - +

+ +
Figure 3: Logic Class Diagram +

-The sections below give more details of each component. +The sections below give more details of each component.

-### UI component +## 2.2 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/AY2223S2-CS2103T-W15-2/tp/blob/master/src/main/java/trackr/ui/Ui.java) -![Structure of the UI Component](images/UiClassDiagram.png) +

+ +
Figure 4: UI Class Diagram +

-The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `TabPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. -The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml) +The `UI` component uses the JavaFX UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/AY2223S2-CS2103T-W15-2/tp/blob/master/src/main/java/trackr/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/AY2223S2-CS2103T-W15-2/tp/blob/master/src/main/resources/view/MainWindow.fxml) The `UI` component, * executes user commands using the `Logic` component. * listens for changes to `Model` data so that the UI can be updated with the modified data. * keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands. -* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`. +* depends on some classes in the `Model` component, as it displays `Order`, `Task` or `Menu` object residing in the `Model`.

-### Logic component +## 2.3 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/AY2223S2-CS2103T-W15-2/tp/blob/master/src/main/java/trackr/logic/Logic.java) Here's a (partial) class diagram of the `Logic` component: - +

+ +
Figure 5: Logic Class Diagram +

How the `Logic` component works: -1. When `Logic` is called upon to execute a command, it uses the `AddressBookParser` class to parse the user command. -1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`. -1. The command can communicate with the `Model` when it is executed (e.g. to add a person). -1. The 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. +1. When `Logic` is called upon to execute a command, it uses the `TrackrParser` class to parse the user command. +2. This results in a `Command` object, which is actually an object of one of its subclasses' (e.g. `AddItemCommand`'s) subclasses (e.g. `AddOrderCommand`). This specific command will then be executed by the `LogicManager`. +3. The command can communicate with the `Model` when it is executed (e.g. to add an order). +4. The result of the command execution is encapsulated as a `CommandResult` object which is returned by `Logic`. + +The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete_order 1")` API call. + +

+ +
Figure 6: Delete Sequence Diagram (Deleting Order) +

+ + +
+ +:information_source: **Note:** -![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) +The lifeline for `DeleteOrderCommandParser` 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 `DeleteCommandParser` 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: - +

+ +
Figure 7: Parser Class Diagram +

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. -### Model component -**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) +* When called upon to parse a user command, the `TrackrParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddOrderCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddOrderCommand`) which the `TrackrParser` returns back as a `Command` object. +* All `XYZCommandParser` classes (e.g., `AddOrderCommandParser`, `DeleteOrderCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing.

- +## 2.4 Model component +**API** : [`Model.java`](https://github.com/AY2223S2-CS2103T-W15-2/tp/blob/master/src/main/java/trackr/model/Model.java) + +

+ +
Figure 8: Model Class Diagram +

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. +* `XYZ` is a placeholder for the specific object (e.g., `Supplier`, `Task`), which are all `Item` objects. +* stores trackr data (all `XYZ` objects which are contained in their respective `UniqueXYZList` object). +* stores currently 'selected' `XYZ` objects (e.g., results of search query) as a **separate filtered list** which is exposed to outsiders as an unmodifiable `ObservableList` that can be viewed (e.g. UI is bound to this list so that the UI automatically updates when the data in the list changes). * stores a `UserPref` object that represents the user’s preferences. This is exposed to 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) +* 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.
+### 2.4.1 Item - +Here is the `Item` class that is what all model objects depend on. -
+

+ +
Figure 9: Item Class Diagram +

+ +Each `ItemList` contains a `UniqueItemList` that stores a list of unique `Items`, which are defined by a model definition (e.g., `Supplier` or `Task` from `ModelEnum`). + +### 2.4.2 Supplier & Customer + +This is the class representation for the `Supplier` and `Customer` class. + +

+ +
Figure 10: Person Class Diagram +

+ +Here is how `Supplier` and `Customer` works: +* `Supplier` and `Customer` inherit off `Person` class, which depends on the `Item` class. +* Each `Person` contains their name, phone number, deadline, email and _tags_. (e.g., `PersonAddress` represents the address) +* The `Supplier` and `Customer` object have their corresponding `List` and `UniqueList` that stores their information. -### Storage component +### 2.4.3 Task -**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java) +This is the class representation for the `Task` class. - +

+ +
Figure 11: Task Class Diagram +

+ +Here is how `Task` works: + +* Each `Task` contains their name, deadline, _status_ and time added (e.g., `TaskName` for task name). +* Each of the attributes inherits off the corresponding `common` classes (e.g., `TaskName` inherit off `Name`). +* The `Task` object have its `List` and `UniqueList`. +* The LocalDateTime object represents the time the task was added to the list (which is used when sorting tasks). + +### 2.4.4 Menu + +This is the class representation for the `Menu` class. + +

+ +
Figure 12: Menu Class Diagram +

+ +Here is how `Menu` works: + +* Each `MenuItem` contains their name, selling price, cost and profit (e.g., `ItemName` for menu's item name). +* The `MenuItem`'s `ItemName` attribute inherit off the corresponding `common` classes (e.g., `ItemName` inherit off `Name`). +* The`ItemProfit` is obtained using `ItemSellingPrice` and `ItemCost`(i.e. `ItemProfit` depends on `ItemSellingPrice` and `ItemCost`). +* `ItemSellingPrice` and `ItemCost` class inherits off the `ItemPrice` class. +* The `MenuItem` object have its `List` called `Menu` and `UniqueList`. +* The `MenuItem` is an attribute of `Order`. + +**Aspect: Choice to provide a menu package:** + +* **Option 1 (our choice):** Separating it into a separate `menu` package. + * Advantage 1: Ensure that the Order name added is a valid item on the menu, which prevents users from accidentally keying in a wrong order name. + * Advantage 2: Allows user to see more details of the menu item in a separate tab (e.g. users can see a selling price, cost price and profit for each item) + * Disadvantage: More time required to implement. + +* **Option 2:** Add item name as an attribute in the `Order` class. + * Advantage: Convenient to implement. + * Disadvantage: Higher chance of conflicts with another developer working on `Order` class. + +### 2.4.5 Order + +This is the class representation for the `Order` class. + +

+ +
Figure 13: Order Class Diagram +

+ +Here is how `Order` works: + +* Each `Order` contains a menu item(from a locally stored menu), customer, quantity, status, deadline and time added (e.g., `OrderStatus` for order's status). +* The menu item and customer each contains attributes as mentioned in their respective section above on how `Menu` and `Customer` works. +* The `Order`'s `OrderDeadline` and `OrderStatus` attribute inherit off the corresponding `common` classes (e.g., `OrderDeadline` inherit off `Deadline`). +* The `Order` object have its `List` called `OrderList` and `UniqueList`. +* The LocalDateTime object represents the time the order was added to the list (which is used when sorting orders). + +## 2.5 Storage component + +**API** : [`Storage.java`](https://github.com/AY2223S2-CS2103T-W15-2/tp/blob/master/src/main/java/trackr/storage/Storage.java) + +

+ +
Figure 14: Storage Class Diagram +

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 Trackr data and user preference data in json format, and read them back into corresponding objects. +* inherits from both `TrackrStorage` 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 +## 2.6 Common classes -Classes used by multiple components are in the `seedu.addressbook.commons` package. +Classes used by multiple components are in the `trackr.commons` package. -------------------------------------------------------------------------------------------------------------------- -## **Implementation** +# 3 Implementation -This section describes some noteworthy details on how certain features are implemented. +This section describes the details on how the commands are implemented. After every command, the new state of all of Trackr's data is saved into the data file. -### \[Proposed\] Undo/redo feature +
-#### Proposed Implementation +:information_source: **Notes about the command format** -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 commands below are in the format `Command`. +* `` represents the action the user can do.
+ e.g. `add`, `edit`and `find` +* `XYZ` represents the type of data the user can input.
+ e.g. `Supplier`, `Order` and `Task` + * Note: If there are specific instances where `XYZ` represents a limited number of types, it would be stated within the description. -* `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. +
-These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +## 3.1 AddXYZCommand -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +The `add` command creates a `XYZ` object and adds it into `XYZList` and `FilteredXYZList`. It also saves into the internal `XYZList`, which stores all the `XYZ` objects, that matches the provided keywords. -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. +The keywords that can be provided are the attributes as seen in the corresponding `XYZ`'s class diagram. +For example, `n/` would be followed by a task name for `AddTaskCommand` and supplier name for `AddSupplierCommand`. -![UndoRedoState0](images/UndoRedoState0.png) +The parser for the `add` command would extract out the arguments corresponding to each particular field. -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. +The following activity diagram summarizes what happens when the user executes the `add` command. -![UndoRedoState1](images/UndoRedoState1.png) +

+ +
Figure 15: Add Command Activity Diagram +

-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`. +
-![UndoRedoState2](images/UndoRedoState2.png) +:information_source: **Information on parameters for add command** -
: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`. +* You can see the specific parameters allowed for [Supplier](UserGuide.md#211-adding-a-supplier-add_supplier), [Order](UserGuide.md#212-adding-an-order-add_order), [Task](UserGuide.md#213-adding-a-task-add_task) and [Menu Item](UserGuide.md#214-adding-a-menu-item-add_item). +* For more information on the specifications of the different attributes, you can look [here](UserGuide.md#61-prefix-summary).
-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. +**Why is it implemented this way** + +The `AddXYZCommand` is an improved version of the original AB3 `AddCommand` by implementing into an abstract class - `AddItemCommand`. +This reduces repeated lines of code and improves ease of implementation for future commands that require adding an item to a list. + +## 3.2 DeleteXYZCommand + +The `delete` command removes a `XYZ` object from internal `FilteredXYZList` and `XYZList`. + +The command only accepts 1 argument without any prefixes. The argument corresponds to the index of `XYZ` in the `FilteredXYZList` that the user wishes to delete using a one-based index. + +The parser for `delete` command extracts the index found in the arguments. If the argument is valid, then zero-based index is used to remove `XYZ` from the `XYZList`. + +The following activity diagram summarizes what happens when the user executes the `delete` command. + +

+ +
Figure 16: Delete Command Activity Diagram +

+ +**Why is it implemented this way** + +The `DeleteXYZCommand` is an improved version of the original _AB3_ `DeleteCommand` by implementing into an abstract class - `DeleteItemCommand`. +This reduces repeated lines of code and improves ease of implementation for future commands that require removing an item from a list. -![UndoRedoState3](images/UndoRedoState3.png) +## 3.3 EditXYZCommand -
: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. +The `edit` command edits a `XYZ` item from the internal `XYZList`. + +This command requires user to key in the index of `XYZ` in the `FilteredXYZList` that the user wishes to edit using a one-based index. + +The keywords that can be provided are the attributes as seen in the corresponding `XYZ`'s class diagram. +For example, `n/` would be followed by a task name for `AddTaskCommand` and supplier name for `AddSupplierCommand`. + +
+ +:information_source: **Information on parameters for edit command** + +* You can see the specific parameters allowed for [Supplier](UserGuide.md#221-editing-a-supplier-edit_supplier), [Order](UserGuide.md#222-editing-an-order-edit_order), [Task](UserGuide.md#223-editing-a-task-edit_task) and [Menu Item](UserGuide.md#224-editing-a-menu-item-edit_item). +* For more information on the specifications of the different attributes, you can look [here](UserGuide.md#61-prefix-summary).
-The following sequence diagram shows how the undo operation works: +The user is required to key in at least one keyword to be edited. + +The parser for `edit` command parses and extracts out the arguments corresponding to each particular field. + +The following activity diagram summarizes what happens when the user executes the `edit` command. +(The rake symbol used in the Figure 17: Edit Command Activity Diagram has been implemented with reference to [this forum](https://forum.plantuml.net/195/is-there-any-support-for-subactivity-or-the-rake-symbol).) + +

+ +
Figure 17: Edit Command Activity Diagram +


+ +

+ +
Figure 18: Edit Item XYZ Activity Diagram +

+ +**Why is it implemented this way** + +The `EditXYZCommand` is an improved version of the original _AB3_ `EditCommand` by implementing into an abstract class - `EditItemCommand`. +This reduces repeated lines of code and improves ease of implementation for future commands that require editing an item in a list. + +## 3.4 FindXYZCommand + +The `find` command finds `XYZ` objects from the internal `XYZList` (which stores all the `XYZ` objects) that matches the provided keywords. + +The keywords that can be provided varies for each command and can be found in the user guide. + +
-![UndoSequenceDiagram](images/UndoSequenceDiagram.png) +:information_source: **Information on parameters for add command** -
:information_source: **Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. +* You can see the specific parameters allowed for [Supplier](UserGuide.md#231-finding-a-supplier-find_supplier), [Order](UserGuide.md#232-finding-an-order-find_order), [Task](UserGuide.md#233-finding-a-task-find_task) and [Menu Item](UserGuide.md#234-finding-a-menu-item-find_item). +* For more information on the specifications of the different attributes, you can look [here](UserGuide.md#61-prefix-summary).
-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 parser for the `find` command would extract out the arguments corresponding to each particular field. + +A `XYZContainsKeywordPredicate` is built upon these fields, which is used to test each `XYZ` object in the `XYZList` on whether they match the keywords provided. + +The following activity diagram summarizes what happens when the user executes the `find` command. + +

+ +
Figure 19: Find Command Activity Diagram +

+ +**Why is it implemented this way** + +The `FindXYZCommand` is an improved version of the original _AB3_ `FindCommand` by implementing into an abstract class - `FindItemCommand`. +This reduces repeated lines of code and improves ease of implementation for future commands that require finding an item in a list. + +The abstract class `ItemDescriptor` stores the details of an item. It provides easier implementation for `XYZContainsKeywordPredicate` classes. + +## 3.5 ListXYZCommand + +The `list` command retrieves all the `XYZ` objects from the `XYZList` and lists them all in the internal `FilteredXYZList`. + +The `FilteredXYZList` is then updated to have all `XYZ` objects, after which it will then to shown to the user. + +The following activity diagram summarizes what happens when the user executes the `list` command. + +

+ +
Figure 20: List Command Activity Diagram +

+ +**Why is it implemented this way** + +The `ListXYZCommand` is an improved version of the original _AB3_ `ListCommand` by implementing into an abstract class - `ListItemCommand`. +This reduces repeated lines of code and improves ease of implementation for future commands that require listing all item of the same data type. + +
-
: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. +## 3.6 SortXYZCommand + +
+ +:information_source: **Information on `XYZ`** + +* `XYZ` for `sort` only refers to tasks and orders.
-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. +The `sort` command sorts `XYZ` objects in the internal `FilteredXYZList` according to a selected criteria. + +The command only accepts 1 argument without the prefix `c/`. This argument is optional, and it corresponds to the criteria the user wishes to sort the list by. + +The parser for the `sort` command would extract out the criteria. If no criteria is given, it will be defaulted to `Status and Deadline`. + +A `SortXYZComparator` is used to define how two `XYZ` objects should be compared and sorted. + +The following activity diagram summarizes what happens when the user executes the `sort` command. + +

+ +
Figure 21: Sort Command Activity Diagram +

+ +**Why is it implemented this way** + +Unlike the other commands, the `SortXYZCommand` does not implement an abstract class like `SortItemCommand`. +It simply extends Command as there are only two sort commands (one for orders and one for tasks). + +Although abstracting out an abstract class would ease implementation for future sort commands, it is not currently planned. +However, this might be changed in the future to follow the syntax of the other commands. + +## 3.7 ClearXYZCommand + +The `clear` command removes **all** `XYZ` object from internal `FilteredXYZList` and `XYZList`. + +The following activity diagram summarizes what happens when the user executes the `delete` command. -![UndoRedoState4](images/UndoRedoState4.png) +

+ +
Figure 22: Clear Command Activity Diagram +

-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. +**Why is it implemented this way** -![UndoRedoState5](images/UndoRedoState5.png) +The `ClearXYZCommand` is an improved version of the original _AB3_ `ClearCommand` by implementing into an abstract class - `ClearItemCommand`. +This reduces repeated lines of code and improves ease of implementation for future commands that require clearing all item from the list. -The following activity diagram summarizes what happens when a user executes a new command: +## 3.8 TabCommand - +`TabCommand` switches tabs on the application through CLI commnads. The user can switch tabs with the `TabCommand` or any of `ListXYZCommand` or `FindXYZCommand` which brings the user to the relevant `XYZ` tab. -#### Design considerations: +The following activity diagram summarizes what happens when the user executes the `tab` command. -**Aspect: How undo & redo executes:** +

+ +
Figure 23: Tab Command Activity Diagram +

-* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. -* **Alternative 2:** Individual command knows how to undo/redo by - itself. - * Pros: Will use less memory (e.g. for `delete`, just save the person being deleted). - * Cons: We must ensure that the implementation of each individual command are correct. -_{more aspects and alternatives to be added}_ +**Why is it implemented this way** -### \[Proposed\] Data archiving +An `Observable` property of `JavaFx` is used so that an `ObservableTabIndex` class can be created. This reduces the coupling between `Logic` which executes the command and `Ui` components. The `Ui` listens for changes in the `ObservableTabIndex`. As `Ui` is only aware of `ObservableTabIndex` but not `Logic`, this also enforces principle of least knowledge. -_{Explain here how the data archiving feature will be implemented}_ +## 3.9 HelpCommand +The `help` command brings up a [HelpWindow](https://github.com/AY2223S2-CS2103T-W15-2/tp/blob/master/src/main/java/trackr/ui/HelpWindow.java), where there is a link to the User Guide of Trackr. The user can also press `F1` or click `Help` the menu bar to bring it up. + +This will allow the users to be able to have a reference to all the commands and explanation within a single webpage. + +**Why is it implemented this way** + +The `HelpCommand` is the same original command as from _AB3_, just with the link updated to this project. + +## 3.10 ExitCommand + +The `exit` command allows the users to exit Trackr via the command line. + +**Why is it implemented this way** + +The `ExitCommand` is the same original command as from _AB3_. -------------------------------------------------------------------------------------------------------------------- -## **Documentation, logging, testing, configuration, dev-ops** +# 4 Appendix + +## 4.1 Documentation, logging, testing, configuration, dev-ops + +Please refer to the respective guides below for other information. * [Documentation guide](Documentation.md) * [Testing guide](Testing.md) @@ -249,88 +629,391 @@ _{Explain here how the data archiving feature will be implemented}_ * [Configuration guide](Configuration.md) * [DevOps guide](DevOps.md) --------------------------------------------------------------------------------------------------------------------- +## 4.2 Product scope -## **Appendix: Requirements** +**Target user profile**: -### Product scope +* Tech-savvy home businesses owners who: + * lists their products online or on their own website + * perform transactions manually without a Point-of-Sale (POS) system + * Lack manpower/ time to track orders and contacts manually + * Has a need to manage a significant number of contacts +* Prefer desktop apps over other types +* Can type fast +* Prefers typing to mouse interactions +* Is reasonably comfortable using CLI apps -**Target user profile**: +**Value proposition**: + +Our application: + +* allows for consolidation of orders, contacts & tasks information which makes it easier to manage them. (but no _real-time automation_) +* serves as a user-friendly alternative to free applications such as Microsoft Excel which may not be catered to their needs and requires tedious formatting. (but no support for custom formatting of interface) +* enables faster contact management compared to a typical mouse/GUI driven app

-* has a need to manage a significant number of contacts -* prefer desktop apps over other types -* can type fast -* prefers typing to mouse interactions -* is reasonably comfortable using CLI apps +## 4.3 User stories -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +**High Priority (Must Have)** +| As a / an …​ | I want to …​ | So that I can…​ | +| ------------ | --------------------------- | ---------------------------------------------------------------------- | +| new user | have an instruction guide | understand how to use the application | +| user | add new orders | have a consolidated place to keep track of my orders | +| user | view all my orders | track my progress in dealing with the orders | +| user | edit my orders | update my order status | +| user | find my orders by keywords | get a specific order without manually searching for it | +| user | delete my orders | remove unwanted old orders | +| user | add new suppliers | easily find them from a consolidated location | +| user | find suppliers by keywords | get the relevant supplier information from a specific supplier contact | +| user | edit my supplier contacts | update past supplier contacts with current information | +| user | delete my supplier contacts | remove supplier contacts not used anymore | +| user | add new tasks | keep track of business tasks from the same application | +| user | find tasks by keywords | get all relevant tasks that are related to plan my schedule | +| user | edit my tasks | update my progress on the task | +| user | delete my tasks | remove old completed tasks | -### User stories +**Medium Priority (Nice to Have)** -Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` +| As a / an …​ | I want to …​ | So that I can…​ | +| -------------- | ---------------------- | --------------------------------------------------------- | +| business owner | add my menu items | add orders based on my menu items | +| business owner | edit my menu items | update my price and cost based on current rates | +| business owner | delete menu items | remove unpopular items that are not sold anymore | +| user | have my orders sorted | view my upcoming orders that are not done yet | +| user | have my tasks sorted | view the most pressing tasks at first glance | +| expert user | be able to export data | keep track of past orders without lagging the application | -| 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 | +**Low Priority (Upcoming)** -*{More to be added}* +| As a / an …​ | I want to …​ | So that I can…​ | +| ------------ | --------------------------- | ------------------------------------------------- | +| expert user | be able to import past data | use the application easily when transferring data | -### Use cases +## 4.4 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 `Trackr` and the **Actor** is the `Home Business Owner`) -**Use case: Delete a person** +**Use case: UC01 - Add a new supplier** **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. Actor chooses to add a supplier +2. Actor enters the command to add a supplier with the required information. +3. Trackr saves the new supplier to the system. +4. Trackr display success message that a new supplier added to the list +5. Actor can see the new supplier information in the contacts list. + Use case ends. + +**Extensions** + +* 2a. Not all the required information was given. + + * 2a1. Trackr shows an error message. + + Use case resumes at step 2. + +* 2b. The Actor adds a supplier that already exists in the list. + + * 2b1. Trackr shows an error message. + + Use case ends. + +**Use case: UC11 - Add a new task** + +(Similar to UC01 except it is for task) + +**Use case: UC21 - Add a new order** + +**MSS** + +1. Actor chooses to add an order. +2. Actor enters the command to add an order with the required information. +3. Trackr saves the new order to the system. +4. Trackr display success message that a new order added to the list +5. Actor can see the new order information in the orders list. + + Use case ends. + +**Extensions** + +* 2a. Not all the required information was given. + + * 4a1. Trackr shows an error message. + + Use case resumes at step 2. + +* 2b. The Actor adds an order that already exists in the list. + + * 4b1. Trackr shows an error message. + Use case ends. +* 2c. The item does not exist in menu + * 2c1. Trackr displays error message. + * 2c2. Actor adds a new menu item(UC31). + + Use case resumes at step 2. + + +**Use case: UC31 - Add a new menu item** + +(Similar to UC01 except it is for menu item) + +**Use case: UC02 - Delete a supplier** + +**MSS** + +1. Actor chooses to delete a supplier. +2. Actor enters the command to list contacts. +3. Trackr shows a list of contacts. +4. Actor enters the command to delete a specific indexed contact from the list. +5. Trackr deletes the contact. + + Use case ends. + **Extensions** * 2a. The list is empty. Use case ends. -* 3a. The given index is invalid. +* 4a. The given index is invalid. - * 3a1. AddressBook shows an error message. + * 4a1. Trackr shows an error message. - Use case resumes at step 2. + Use case resumes at step 4. + +* 4b. No index was given. + * 4b1. Trackr shows an error message. + + Use case resumes at step 4. + + +**Use case: UC12 - Delete a task** + +(Similar to UC02 except it is for task) + +**Use case: UC22 - Delete an order** + +(Similar to UC02 except it is for order) + +**Use case: UC32 - Delete a menu item** + +(Similar to UC02 except it is for menu item) + +**Use case: UC03 - Edit a supplier** + +**MSS** + +1. Actor chooses to edit a supplier +2. Actor enters the command to list contacts. +3. Trackr shows a list of contacts. +4. Actor enters an edit contact command for a specific contact and the updated information. +5. Trackr updates the contact details with the new information. + + Use case ends. + +**Extensions** + +* 2a. The list is empty. + + Use case ends. + +* 3a. The Actor enters an invalid index. + + * 3a1. Trackr displays an error message. + + Use case resumes at step 3. -*{More to be added}* +* 3b. The Actor enters invalid information. -### Non-Functional Requirements + * 3b1. Trackr displays an error message. -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 resumes at step 3. -*{More to be added}* +* 3c. The Actor does not enter any information. -### Glossary + * 3c1. Trackr displays an error message. + + Use case resumes at step 3. + +**Use case: UC13 - Edit a task** + +(Similar to UC03 except it is for task) + +**Use case: UC23 - Edit an order** + +(Similar to UC03 except it is for order) + +**Use case: UC33 - Edit a menu item** + +(Similar to UC03 except it is for menu item) + +**Use case: UC04 - Finding a supplier** + +**MSS** + +1. Actor chooses to find a supplier +2. Actor enter the command to find a supplier with the desired search criteria. +3. Trackr searches for suppliers that match the given criteria. +4. Trackr displays a list of suppliers that match the criteria. + + Use case ends. + +**Extensions** + +* 2a. No search criteria was given. + + * 2a1. Trackr displays an error message. + + * Use case resumes at step 2. + +* 4a. No supplier matches the given search criteria. + + * 4a1. Trackr display an empty list. + + Use case ends. + +**Use case: UC14 - Find a task** + +(Similar to UC04 except it is for task) + +**Use case: UC24 - Find an order** + +(Similar to UC04 except it is for order) + +**Use case: UC34 - Find a menu item** + +(Similar to UC04 except it is for menu item) + +**Use case: UC15 - Sort tasks** + +**MSS** + +1. Actor chooses to sort tasks. +2. Actor enters the command with the desired sorting criteria. +3. Trackr sorts tasks according to the criteria. +4. Trackr displays a list of tasks that are sorted. + + Use case ends. + +**Extensions** + +* 2a. No sorting criteria was given. + + * 2a1. Trackr sorts tasks based on default criteria. + + * Use case ends. +* 2b. Invalid sorting criteria was given. + * 2b1. Trackr displays error message. + + Use case resumes at step 2. + +**Use case: UC25 - Sort orders** + +(Similar to UC15 except it is for order) + +**Use case: UC50 - Switch to another tab** + +**MSS** + +1. Actor enters the command to switch to another tab. +2. Actor interacts with the tab menu. +3. Trackr switches to the target tab. + + Use case ends. + +## 4.5 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 200 supplier and customer contacts without a noticeable sluggishness in performance for typical usage. +3. Should be able to hold up to 1000 order details without a noticeable sluggishness in performance for typical usage. +4. Should be able to hold up to 200 tasks without a noticeable sluggishness in performance for typical usage. +5. 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. +6. Should store data locally only. + +-------------------------------------------------------------------------------------------------------------------- + +## 4.6 Glossary * **Mainstream OS**: Windows, Linux, Unix, OS-X -* **Private contact detail**: A contact detail that is not meant to be shared with others +* **CLI**: Command-Line Interface +* **GUI**: Graphical User Interface +* **AB3**: AddressBook-Level3 (The parent project this is based off on) +* **Real-time automation**: Automatically update tasks or orders with deadlines that have passed as done or overdue. +* **Supplier**: Supplier refers to someone whom the user seasonally or frequently orders goods from +* **Customer**: Customer refers to someone whom the user receives an order from +* **Order**: Order refers to the customers' orders the user accepts +* **Task**: Task refers to any to-dos the user may have, it need not be related to suppliers or orders (For instance, it can be about tidying inventory) +* **Menu Item**: Menu Item refers to any inventory/ stock that the user is selling to customers +* **Tag**: Tags are associated with suppliers, users can tag the supplier with any keyword they want, number of tags are not restricted +* **Status**: Statuses are associated with tasks and orders, one entry of task/order can only have one status and the type of status that can be added is restricted

+ +-------------------------------------------------------------------------------------------------------------------- + +## 4.7 Planned Enhancements + +**1.** Ensure all the commands follow the standard format so that it is easier for the user to remember.
+Currently, some commands do not follow the standard format which requires a prefix in front of each parameter. +For instance, `find_supplier` is of the format `find_supplier NAME` instead of `find_supplier n/NAME`.
+The format is shown below. +
+ <command> + + <index> + + <prefix/paramater> <prefix/paramater> ... +
+
+ +**2.** Provide better success messages for edit commands.
+Currently, the edit commands shows ambiguous success or error messages to the user.
+For example editing a task return the success message, `Edited task: task`. +We suggest a clearer and more comprehensive message such as `Edited task: ` where `` represents the edited data.
+Specifically, `Edited task: Buy eggs; Deadline:01 January 2023; Status: Not Done`. + +Proposed implementation: +Currently, all the EditXYZCommand extends from the EditItemCommand and uses the success message format (`Edited %s: %1$s`) defined in EditItemCommand class. +When an edit command is executed successfully, we generate the success message by replacing `%s` with the item type and `%1$s` with the string representation of the edited item object. +We plan to change the format of the success message (by replacing `%1$s` to `%s`) to print out the entire string of instead of only printing out the first word which is the item name. + +**3.** Fix error messages for commands.
+Currently, some commands have an issue with their error message.
+For example, if there is a duplicate from this command `add_order on/Chocolate Cookies q/10 d/10/10/2023 n/Ben p/11111111 a/Ben Street`, +the error message should be `This Order already exists in the order list`. +However, currently, this error message is shown instead: `This Order already exists in the Chocolate Cookies; 10; Deadline: 10 October 2023; Status: Not Delivered; Customer: Ben; Phone: 11111111; Address: Ben Street list`. + +**4.** Improve validity checks for menu item's `PRICE` and `COST`.
+Currently, there is no comprehensive validity check for these two parameters, which might result in bugs shown within the application.
+Both `ItemSellingPrice` class (which represents the `PRICE` and `ItemCost` class (representing the `COST`) extends from the `ItemPrice` class (as seen in this [Class Diagram](#244-menu)). +Thus, both `PRICE` and `COST` are checked with the same `ItemPrice::isValidPrice(String)` method. +We plan to improve on this feature flaw by modifying this `isValidPrice(String)` method to check and limit the price and cost entered to be `1000` or less, +so as to prevent a situation where an issue with the calculation occurs when excessively large numbers are keyed in. + +**5.** Improve validity check for order's and task's `DEADLINE`.
+Currently, the validity check for deadlines is not strict enough. +For example, users are able to enter deadlines that are far away in the past like `01/01/1800`. +Therefore, we plan to improve on this by only allowing users to key in deadlines that falls within the past 10 years. +Since both `OrderDeadline` and `TaskDeadline` extends the common class `Deadline` and `Deadline::isValidDeadline` is used to check the validity of both parameters, +we plan to simply tweak the implementation of `Deadline::isValidDeadline` for stricter checks on deadlines entered. + +**6.** Fix error in `find_order`. +Currently, `find_order` only works on the last field that is typed into the command. Hence, it is ignoring the other fields. +We plan to improve this feature to match `find_task` command where it would match the multiple fields if they are present in an `AND` format. +Specifically, `find_order q/2 d/01/01/2024` would list all orders that have quantity of `2` and a deadline of `01/01/2024`. -------------------------------------------------------------------------------------------------------------------- -## **Appendix: Instructions for manual testing** +## 4.8 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. +
+ +:information_source: **Note** + +These instructions only provide a starting point for testers to work on and testers are expected to do more exploratory testing.
@@ -347,31 +1030,560 @@ testers are expected to do more *exploratory* testing. 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. + Expected: The most recent window size and location is retained. + +1. Shutdown + + 1. Execute the command `exit` or close the window using GUI by clicking the cross on the top-right hand corner. + Expected: The window will close and all the data will be saved.

+ +### Adding a supplier + +**Test Case 1** + +Context: There is **no** contact with the phone number `11111111` in Trackr. + +Action: Execute command `add_supplier n/Ben p/11111111 a/Ben Street e/ben@gmail.com` + +Expected Outcome: + +* New supplier `Ben` is added into the supplier list. +* Success message shown in command result box.
+ Specifically, `New Supplier added: Ben; Phone: 11111111; Email: ben@gmail.com; Address: Ben Street` + +**Test Case 2** + +Context: There **is** a contact with the phone number `11111111` in Trackr. + +Action: Execute command `add_supplier n/Ben p/11111111 a/Ben Street e/ben@gmail.com` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `This Supplier already exists in the Supplier list` + +### Adding a task + +**Test Case 1** + +Context: There is **no** task with the name `Buy yeast` and deadline `10/10/2023` in Trackr. + +Action: Execute command `add_task n/Buy yeast d/10/10/2023` + +Expected Outcome: + +* New task `Buy yeast` is added into the task list. +* Success message shown in command result box.
+ Specifically, `New Task added: Buy yeast; Deadline: 10 October 2023; Status: Not Done` + +**Test Case 2** + +Context: There **is** a task with the name `Buy yeast` and deadline `10/10/2023` in Trackr. + +Action: Execute command `add_task n/Buy yeast d/10/10/2023` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `This Task already exists in the Task list` + +### Adding an order + +**Test Case 1** + +Context: There is **no** order with the same customer, order item, deadline and quantity in Trackr. + +Action: Execute command `add_order on/Chocolate Cookies q/10 d/10/10/2023 n/Ben p/11111111 a/Ben Street` + +Expected Outcome: + +* New order is added into the order list. +* Success message shown in command result box.
+ Specifically, `New Order added: Chocolate Cookies; 10; Deadline: 10 October 2023; Status: Not Delivered; Customer: Ben; Phone: 11111111; Address: Ben Street` + +**Test Case 2** + +Context: There **is** an order with the same customer, order item, deadline and quantity in Trackr. + +Action: Execute command `add_order on/Chocolate Cookies q/10 d/10/10/2023 n/Ben p/11111111 a/Ben Street` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `This Order already exists in the Chocolate Cookies; 10; Deadline: 10 October 2023; Status: Not Delivered; Customer: Ben; Phone: 11111111; Address: Ben Street list`
+ Note that there is an issue with the error message now and will be fixed in the future update. + +### Adding a menu item + +**Test Case 1** + +Context: There is **no** menu item with the name `Vanilla Cake` in Trackr. + +Action: Execute command `add_item n/Vanilla Cake pr/40 c/25` + +Expected Outcome: + +* New menu item `Vanilla Cake` is added into the menu. +* Success message shown in command result box.
+ Specifically, `New Menu Item added: Vanilla Cake; Selling Price: $40.00; Cost: $25.00; Profit: $15.00` + +**Test Case 2** + +Context: There **is** a menu item with the name `Vanilla Cake` in Trackr. + +Action: Execute command `add_item n/Vanilla Cake pr/40 c/25` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `This Menu Item already exists in the Menu Item list` + +### Editing supplier + +**Test Case 1** + +Context: There **is** a supplier at index `1` and **no** suppliers with the new phone number `11111111` in Trackr. + +Action: Execute command `edit_supplier 1 p/11111111` + +Expected Outcome: + +* Edited **first** supplier with phone number `11111111`. +* Success message shown in command result box.
+ Specifically, `Edited supplier: supplier` + +**Test Case 2** + +Context: There **is** a supplier at index `1` and **a** supplier with the new phone number `11111111` in Trackr. + +Action: Execute command `edit_supplier 1 p/11111111` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `This supplier already exists in the supplier list.` + +**Test Case 3** + +Context: There **is** no supplier at index `5` in Trackr. + +Action: Execute command `edit_supplier 5 p/11111111` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `The supplier index provided is invalid` + +### Editing task + +**Test Case 1** + +Context: There **is** a task at index `1` and **no** supplier with the new name `Buy eggs` and deadline `10/10/2023` in Trackr. + +Action: Execute command `edit_task 1 n/Buy eggs d/10/10/2023` + +Expected Outcome: + +* Edited **first** task with name `Buy eggs` and deadline `10/10/2023`. +* Success message shown in command result box.
+ Specifically, `Edited task: task` + +**Test Case 2** + +Context: There **is** a task at index `1` and **a** supplier with the new name `Buy eggs` and deadline `10/10/2023` in Trackr. + +Action: Execute command `edit_task 1 n/Buy eggs d/10/10/2023` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `This task already exists in the task list.` + +**Test Case 3** + +Context: There **is** no task at index `5` in Trackr. + +Action: Execute command `edit_task 5 n/Buy eggs d/10/10/2023` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `The task index provided is invalid` + +### Editing order + +**Test Case 1** + +Context: There **is** an order at index `1` and **no** same details as edited order in Trackr. + +Action: Execute command `edit_order 1 a/John Street` + +Expected Outcome: + +* Edited **first** order with customer address `John Street`. +* Success message shown in command result box.
+ Specifically, `Edited order: order` + +**Test Case 2** + +Context: There **is** an order at index `1` and **an** order with the same details as the edited order in Trackr. + +Action: Execute command `edit_order 1 a/John Street` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `This order already exists in the order list.` + +**Test Case 3** + +Context: There **is** no order at index `5` in Trackr. + +Action: Execute command `edit_task 5 n/Buy eggs d/10/10/2023` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `The order index provided is invalid` + +### Editing menu item + +**Test Case 1** + +Context: There **is** a menu item at index `1` and **no** menu items with same name `Chocolate Cookies` in Trackr. + +Action: Execute command `edit_item 1 n/Chocolate Cookies` + +Expected Outcome: + +* Edited **first** menu item with name `Chocolate Cookies`. +* Success message shown in command result box.
+ Specifically, `Edited menu item: menu item` + +**Test Case 2** + +Context: There **is** a menu item at index `1` and **a** menu item with the same name `Chocolate Cookies` in Trackr. + +Action: Execute command `edit_item 1 n/Chocolate Cookies` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `This menu item already exists in the menu item list.` + +**Test Case 3** + +Context: There **is** no menu item at index `5` in Trackr. + +Action: Execute command `edit_item 1 n/Chocolate Cookies` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `The menu item index provided is invalid` + +### Find supplier + +**Test Case 1** + +Context: There **is** a supplier with name containing `John` in Trackr. + +Action: Execute command `find_supplier John` + +Expected Outcome: + +* Shows the **1** supplier with name containing `John`. +* Success message shown in command result box.
+ Specifically, `1 suppliers listed!` + +### Find task + +**Test Case 1** + +Context: There **is** a task with name containing `Buy` in Trackr. + +Action: Execute command `find_task n/Buy` + +Expected Outcome: + +* Shows the **1** task with name containing `Buy`. +* Success message shown in command result box.
+ Specifically, `1 tasks listed!` + +### Find order + +**Test Case 1** + +Context: There **is** an order with customer name containing `John` in Trackr. + +Action: Execute command `find_order n/John` + +Expected Outcome: + +* Shows the **1** order with customer name containing `Buy`. +* Success message shown in command result box.
+ Specifically, `1 orders listed!` + +### Find menu item + +**Test Case 1** + +Context: There **is** a menu item with name containing `Chocolate` in Trackr. + +Action: Execute command `find_item Chocolate` + +Expected Outcome: + +* Shows the **1** menu item with name containing `Chocolate`. +* Success message shown in command result box.
+ Specifically, `1 menu items listed!` + +### Delete supplier + +**Test Case 1** + +Context: There **is** a supplier at index `1` in Trackr. + +Action: Execute command `delete_supplier 1` + +Expected Outcome: + +* Deleted the **first** supplier. +* Success message shown in command result box. + +**Test Case 2** + +Context: There **is** no supplier at index `5` in Trackr. + +Action: Execute command `delete_supplier 5` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `The supplier index provided is invalid` + +### Delete task + +**Test Case 1** + +Context: There **is** a task at index `1` in Trackr. + +Action: Execute command `delete_task 1` + +Expected Outcome: + +* Deleted the **first** task. +* Success message shown in command result box. + +**Test Case 2** + +Context: There **is** no task at index `5` in Trackr. + +Action: Execute command `delete_task 5` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `The task index provided is invalid` + +### Delete order + +**Test Case 1** + +Context: There **is** an order at index `1` in Trackr. + +Action: Execute command `delete_order 1` + +Expected Outcome: + +* Deleted the **first** order. +* Success message shown in command result box. + +**Test Case 2** + +Context: There **is** no order at index `5` in Trackr. + +Action: Execute command `delete_order 5` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `The order index provided is invalid` + +### Delete menu item + +**Test Case 1** + +Context: There **is** a menu item at index `1` in Trackr. + +Action: Execute command `delete_item 1` + +Expected Outcome: + +* Deleted the **first** menu item. +* Success message shown in command result box. + +**Test Case 2** + +Context: There **is** no menu item at index `5` in Trackr. + +Action: Execute command `delete_item 5` + +Expected Outcome: + +* Error message shown in command result box.
+ Specifically, `The menu item index provided is invalid` + +### Sort task + +**Test Case 1** + +Context: There are tasks not sorted by name in Trackr. + +Action: Execute command `sort_task c/Name` + +Expected Outcome: + +* Sorted tasks by name in alphabetical order (Task name starting with `A` at the top and `Z` at the bottom). +* Success message shown in command result box.
+ Specifically, `Tasks sorted!` + +### Sort order + +**Test Case 1** + +Context: There are orders not sorted by order item name in Trackr. + +Action: Execute command `sort_order c/Name` + +Expected Outcome: + +* Sorted orders by menu item name in alphabetical order (Menu item name starting with `A` at the top and `Z` at the bottom). +* Success message shown in command result box.
+ Specifically, `Orders sorted!` + +### List all suppliers + +**Test Case 1** + +Context: None. + +Action: Execute command `list_supplier` + +Expected Outcome: + +* List and shows all suppliers. +* Success message shown in command result box.
+ Specifically, `Listed all suppliers` + +### List all tasks + +**Test Case 1** + +Context: None. + +Action: Execute command `list_task` + +Expected Outcome: + +* List and shows all tasks. +* Success message shown in command result box.
+ Specifically, `Listed all tasks` + +### List all orders + +**Test Case 1** + +Context: None. + +Action: Execute command `list_order` + +Expected Outcome: + +* List and shows all orders. +* Success message shown in command result box.
+ Specifically, `Listed all orders` + +### List all menu items + +**Test Case 1** + +Context: None. + +Action: Execute command `list_menu` + +Expected Outcome: + +* List and shows all menu items. +* Success message shown in command result box.
+ Specifically, `Listed all menu items` + +### Clear all suppliers + +**Test Case 1** + +Context: None. + +Action: Execute command `clear_supplier` + +Expected Outcome: + +* Cleared all suppliers. +* Success message shown in command result box.
+ Specifically, `Supplier list has been cleared!` + +### Clear all tasks + +**Test Case 1** + +Context: None. + +Action: Execute command `clear_task` + +Expected Outcome: + +* Cleared all tasks. +* Success message shown in command result box.
+ Specifically, `Task list has been cleared!` + +### Clear all orders + +**Test Case 1** + +Context: None. + +Action: Execute command `clear_order` + +Expected Outcome: + +* Cleared all orders. +* Success message shown in command result box.
+ Specifically, `Order list has been cleared!` -1. _{ more test cases …​ }_ +### Clear all menu items -### Deleting a person +**Test Case 1** -1. Deleting a person while all persons are being shown +Context: None. - 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. +Action: Execute command `clear_menu` - 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. +Expected Outcome: - 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. +* Cleared all menu items. +* Success message shown in command result box.
+ Specifically, `Menu Item list has been cleared!` - 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous. +### Switch tabs -1. _{ more test cases …​ }_ +**Test Case 1** -### Saving data +Context: None. -1. Dealing with missing/corrupted data files +Action: Execute command `tab t/MENU` - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ +Expected Outcome: -1. _{ more test cases …​ }_ +* Switched to the menu tab. +* Success message shown in command result box.
+ Specifically, `Switched tab.` diff --git a/docs/Documentation.md b/docs/Documentation.md index 3e68ea364e7..599a0f24487 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -22,7 +22,7 @@ title: Documentation guide **Diagrams:** -* See the [_[se-edu/guides] **Using PlantUML**_](https://se-education.org/guides/tutorials/plantUml.html) +* See the guide [_[se-edu/guides] **Using PlantUML**_](https://se-education.org/guides/tutorials/plantUml.html) **Converting a document to the PDF format:** diff --git a/docs/SettingUp.md b/docs/SettingUp.md index 275445bd551..ae0f3848737 100644 --- a/docs/SettingUp.md +++ b/docs/SettingUp.md @@ -23,7 +23,7 @@ If you plan to use Intellij IDEA (highly recommended): 1. **Import the project as a Gradle project**: Follow the guide [_[se-edu/guides] IDEA: Importing a Gradle project_](https://se-education.org/guides/tutorials/intellijImportGradleProject.html) to import the project into IDEA.
:exclamation: Note: Importing a Gradle project is slightly different from importing a normal Java project. 1. **Verify the setup**: - 1. Run the `seedu.address.Main` and try a few commands. + 1. Run the `trackr.Main` and try a few commands. 1. [Run the tests](Testing.md) to ensure they all pass. -------------------------------------------------------------------------------------------------------------------- @@ -45,11 +45,4 @@ If you plan to use Intellij IDEA (highly recommended): 1. **Learn the design** - When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [AddressBook’s architecture](DeveloperGuide.md#architecture). - -1. **Do the tutorials** - These tutorials will help you get acquainted with the codebase. - - * [Tracing code](tutorials/TracingCode.md) - * [Adding a new command](tutorials/AddRemark.md) - * [Removing fields](tutorials/RemovingFields.md) + When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [Trackr’s architecture](DeveloperGuide.md#21-architecture). diff --git a/docs/Testing.md b/docs/Testing.md index 8a99e82438a..c5c8b249e62 100644 --- a/docs/Testing.md +++ b/docs/Testing.md @@ -29,8 +29,8 @@ There are two ways to run tests. This project has three types of tests: 1. *Unit tests* targeting the lowest level methods/classes.
- e.g. `seedu.address.commons.StringUtilTest` + e.g. `trackr.commons.StringUtilTest` 1. *Integration tests* that are checking the integration of multiple code units (those code units are assumed to be working).
- e.g. `seedu.address.storage.StorageManagerTest` -1. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
- e.g. `seedu.address.logic.LogicManagerTest` + e.g. `trackr.storage.StorageManagerTest` +1. Hybrids of unit and integration tests. These test are checking multiple code units as well as how they are connected together.
+ e.g. `trackr.logic.LogicManagerTest` diff --git a/docs/UserGuide.md b/docs/UserGuide.md index e7df68b01ea..c7908a97caa 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -3,191 +3,1699 @@ layout: page title: User Guide --- -AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps. - -* Table of Contents -{:toc} +# Overview + +Introducing **Trackr - the ultimate desktop application** designed to simplify the delivery management process for your home business! + +With Trackr, you can seamlessly manage your suppliers, orders and tasks. + +It utilizes a Command Line Interface (CLI), while still enjoying the benefits of a user-friendly Graphical User Interface (GUI). + +Say goodbye to the hassle of Excel and the stress of time constraints! Whether you're a busy home business owner or simply looking for an efficient and streamlined solution, Trackr is the perfect fit for you. +Experience the convenience of delivery management like never before with Trackr. + +# About This Guide + +This guide shows you the relevant information for setting up and using Trackr to manage your suppliers, orders and tasks. + +You can click on any of the links below to navigate to the respective sections for more information. + +# Table of Contents + + +* [1 Quick start](#1-quick-start) + * [1.1 Prerequisites](#11-prerequisites) + * [1.1.1 Java](#111-java) + * [1.1.2 Glossary](#112-glossary) + * [1.2 Installation](#12-installation) + * [1.3 Utilisation](#13-utilisation) + * [1.4 Navigating the User Interface](#14-navigating-the-user-interface) + * [1.4.1 Home Page](#141-home-page) + * [1.4.2 Data Tab](#142-data-tab) +* [2 Commands](#2-commands) + * [2.0 Checking for duplicates](#20-checking-for-duplicates) + * [2.1 Add](#21-add) + * [2.1.1 Adding a supplier: `add_supplier`](#211-adding-a-supplier-add_supplier) + * [2.1.2 Adding an order: `add_order`](#212-adding-an-order-add_order) + * [2.1.3 Adding a task: `add_task`](#213-adding-a-task-add_task) + * [2.1.4 Adding a menu item: `add_item`](#214-adding-a-menu-item-add_item) + * [2.2 Edit](#22-edit) + * [2.2.1 Editing a supplier: `edit_supplier`](#221-editing-a-supplier-edit_supplier) + * [2.2.2 Editing an order: `edit_order`](#222-editing-an-order-edit_order) + * [2.2.3 Editing a task: `edit_task`](#223-editing-a-task-edit_task) + * [2.2.4 Editing a menu item: `edit_item`](#224-editing-a-menu-item-edit_item) + * [2.3 Find](#23-find) + * [2.3.1 Finding a supplier: `find_supplier`](#231-finding-a-supplier-find_supplier) + * [2.3.2 Finding an order: `find_order`](#232-finding-an-order-find_order) + * [2.3.3 Finding a task: `find_task`](#233-finding-a-task-find_task) + * [2.3.4 Finding a menu item: `find_item`](#234-finding-a-menu-item-find_item) + * [2.4 Delete](#24-delete) + * [2.4.1 Deleting a supplier: `delete_supplier`](#241-deleting-a-supplier-delete_supplier) + * [2.4.2 Deleting an order: `delete_order`](#242-deleting-an-order-delete_order) + * [2.4.3 Deleting a task: `delete_task`](#243-deleting-a-task-delete_task) + * [2.4.4 Deleting a menu item: `delete_item`](#244-deleting-a-menu-item-delete_item) + * [2.5 Sort](#25-sort) + * [2.5.1 Sorting a task: `sort_task`](#251-sorting-a-task-sort_task) + * [2.5.2 Sorting a order: `sort_order`](#252-sorting-a-order-sort_order) + * [2.6 List](#26-list) + * [2.6.1 Listing all suppliers: `list_supplier`](#261-listing-all-suppliers-list_supplier) + * [2.6.2 Listing all orders: `list_order`](#262-listing-all-orders-list_order) + * [2.6.3 Listing all tasks: `list_task`](#263-listing-all-tasks-list_task) + * [2.6.4 Listing all menu items: `list_menu`](#264-listing-all-menu-items-list_menu) + * [2.7 Clear](#27-clear) + * [2.7.1 Clear all supplier entries: `clear_supplier`](#271-clear-all-supplier-entries-clear_supplier) + * [2.7.2 Clear all order entries: `clear_order`](#272-clear-all-order-entries-clear_order) + * [2.7.3 Clear all task entries: `clear_task`](#273-clear-all-task-entries-clear_task) + * [2.7.4 Clear all menu item entries: `clear_menu`](#274-clear-all-menu-item-entries-clear_menu) + * [2.8 Switch](#28-switch) + * [2.8.1 Switching tabs: `tab`](#281-switching-tabs-tab) + * [2.9 Others](#29-others) + * [2.9.1 Viewing help: `help`](#291-viewing-help-help) + * [2.9.2 Uploading a csv file](#292-uploading-a-csv-file) + * [2.9.3 Exiting the program: `exit`](#293-exiting-the-program-exit) +* [3 Data](#3-data) + * [3.1 Saving of data](#31-saving-of-data) + * [3.2 Editing the data file](#32-editing-the-data-file) +* [4 Upcoming features `[coming soon]`](#4-upcoming-features-coming-soon) +* [5 FAQ](#5-faq) +* [6 Summary](#6-summary) +* [6.1 Prefix Summary](#61-prefix-summary) + * [6.2 Command Summary](#62-command-summary) + -------------------------------------------------------------------------------------------------------------------- -## Quick start +# 1 Quick start + +## 1.1 Prerequisites + +### 1.1.1 Java + +Ensure you have [Java `11`](https://www.oracle.com/sg/java/technologies/javase/jdk11-archive-downloads.html) or above installed. +Java is the language that your computer uses to understand Trackr. + +
+ +:bulb: **Tip** + +**How to check your current Java version:** + +**Step 1.** Open up **Command Prompt** (Windows) or **Terminal** (Mac and Linux). + +**Step 2.** Type and run the command `java -version`. + +**Step 3.** Check the version number provided (`xxx`) is at least `11`. + +An example is shown below. + + ``` + > java -version + java version "xxx" + ``` + +
+ +
+ +### 1.1.2 Glossary + +* **Mainstream OS**: Windows, Linux, Mac +* **CLI**: Command-Line Interface +* **GUI**: Graphical User Interface +* **Supplier**: Supplier refers to someone whom the user seasonally or frequently orders goods from +* **Customer**: Customer refers to someone whom the user receives an order from +* **Order**: Order refers to the customers' orders the user accepts +* **Task**: Task refers to any to-dos the user may have, it need not be related to suppliers or orders (For instance, it can be about tidying inventory) +* **Menu Item**: Menu Item refers to any inventory/ stock that the user is selling to customers. +* **Tag**: Tags are associated with suppliers, users can tag the supplier with any keyword they want, number of tags are not restricted +* **Status**: Statuses are associated with tasks and orders, one entry of task/order can only have one status and the type of status that can be added is restricted + +## 1.2 Installation + +**Step 1.** Download the latest `trackr.jar` file from [here](https://github.com/AY2223S2-CS2103T-W15-2/tp/releases). + +**Step 2.** Copy the file to the folder where you will use Trackr. + +**Step 3.** Double-click on the `trackr.jar` file. + +
+ +:bulb: **Tip** + +**Trackr does not open?** + +**Step 1.** Open a command terminal. + +**Step 2.** Type in `java -jar ` (Keep in mind of the space at the end). + +**Step 3.** Drag and drop `trackr.jar` into the command terminal. + +**Step 4.** Press enter and execute the command. + +An example of the final command is displayed below. + + ``` + > java -jar xxxx/xxxx/trackr.jar + ``` + +
+ +A GUI similar to the below should appear in a few seconds. + +

+ +
Figure 1.2.1: Home Tab UI +

+ +The UI has the following areas: + +* Tabs +* Sales (Total Revenue and Profit) +* Simplified Orders +* Simplified Tasks +* Simplified Menu +* Command Result Screen +* Command Input Box +* Data List + +For more information on how to navigate the User Interface (UI), you can go [here](#14-navigating-the-user-interface) + +

+ +
Figure 1.2.2: Labeled Home Tab UI +

+ +

+ +
Figure 1.2.3: Labeled Data Tab UI +

+ +
+ +:warning: **Warning**
+ +When you open the application for the first time on your device, `Trackr` will create a `data` folder with `trackr.json` data file inside. + +If you are a _new_ user, do not edit this file. + +Otherwise, you can check out how to edit it [here](#32-editing-the-data-file) + +
+ +## 1.3 Utilisation + +**Step 1:** Enter your command into the Command Input Box. + +You can try the following commands to add a task: + +* `tab t/TASKS` +* `add_task n/Buy eggs d/12/12/2040` + +**Step 2:** Observe the changes made to the Task list. Look at the message displayed inside the Command Result Screen to see the results of your command. + +
+ +:bulb: **Tip** + +You can find out all the usable commands [here](#2-commands) or return to the [Table of Contents](#table-of-contents) to find your desired command. + +
+ +## 1.4 Navigating the User Interface + +Here, we will explain how you can navigate around our application. + +**Command Input Box** + +You can input your commands into the command input box, press **Enter** to execute your commands. A result message would be shown at the Command Result Screen. + +If there is an error with the command, the input would turn **red** and you can refer to the Command Result Screen for help. + +**Command Result Screen** + +Command result screen shows the success message for your command that executed successfully. + +Otherwise, it would show an error message with hints on what was missing / invalid in the command that you have entered. +Refer to the error message or look back at this User Guide for more information on how to correct your command input. -1. Ensure you have Java `11` or above installed in your Computer. +**Tabs** -1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +We provide multiple tabs for you to use. This includes the home page, with the data tabs that display your data for the respective information (i.e. Order, Tasks, Contacts, Menu). -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +You can navigate between them by clicking on the tab itself, or input the command `tab t/` followed with the respective tab name. -1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar addressbook.jar` command to run the application.
- A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- ![Ui](images/Ui.png) +For more information about the `tab` command, you can go [here](#281-switching-tabs-tab). -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.4.1 Home Page - * `list` : Lists all contacts. +Upon starting the application, you will see a convenient dashboard with summarised information that you need for your business. - * `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. +**Sales (Total Revenue and Profit)** - * `delete 3` : Deletes the 3rd contact shown in the current list. +Trackr keeps track of all orders submitted and automatically updates the total revenue and profit that your business has generated with us. - * `clear` : Deletes all contacts. +**Simplified Orders** - * `exit` : Exits the app. +Order list shows a condensed preview of your outstanding orders, with their relevant details (i.e. customer name, promised delivery date and your progress). -1. Refer to the [Features](#features) below for details of each command. +**Simplified Tasks** + +Task list shows all outstanding miscellaneous tasks that you may have for your current operations. + +**Simplified Menu** + +Menu list shows all the items that you have up for sale currently, with their details (cost price, selling price and profit margin) at a glance. + +### 1.4.2 Data Tab + +**Data List** + +Data list contains all of your respective data labelled out in full. The layout of the respective data information can be seen below: + +* Orders + +`Orders` contains order information, with their data represented by their respective cards. + +Order Card consists of the following components: + +1. Index of the order +2. Name of the order item (which should correspond to its respective Menu item name) +3. Quantity of order item +4. Deadline for delivery of the order +5. Status of the order item +6. Customer name +7. Customer phone number +8. Customer address + +

+ +
Figure 1.4.2.1: Labeled Order Card +

+ +* Tasks + +`Tasks` contains task information, with their data represented by their respective cards. + +Task Card consists of the following components: + +1. Index of the task +2. Name of the task +3. Deadline of the task +4. Status of the task + +

+ +
Figure 1.4.2.2: Labeled Task Card +

+ +* Contacts + +`Contacts` contains supplier information, with their data represented by their respective cards. + +Supplier Contact Card consists of the following components: + +1. Index of the supplier +2. Name of the supplier +3. Tags related to the supplier +4. Phone number of the supplier +5. Address of the supplier +6. Email of the supplier + +

+ +
Figure 1.4.2.3: Labeled Supplier Card +

+ +* Menu + +`Menu` contains menu items, with their data represented by their respective cards. + +Menu Item Card consists of the following components: + +1. Index of the menu item +2. Menu item name +3. Selling price of the menu item +4. Cost of making the menu item +5. Profit from selling the menu item (Profit = Selling Price - Cost) + +

+ +
Figure 1.4.2.4: Labeled Menu Card +

-------------------------------------------------------------------------------------------------------------------- -## Features +
+ +# 2 Commands
-**:information_source: Notes about the command format:**
+:information_source: **Notes about the command format:** + +* All command keywords must be in lower-case and are case-sensitive.
+ e.g. For add supplier command:
+ :heavy_check_mark: `add_supplier` is valid.
+ :x: `ADD_SUPPLIER` and `Add_Supplier` are invalid.
-* 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`. +* Prefixes of the parameters must be in lower-case and are case-sensitive.
+ e.g. `n/` in `add_s n/NAME` is case-sensitive
+ :heavy_check_mark: `n/` is a valid prefix.
+ :x: `N/` is an invalid prefix.
+ +* Words in `UPPER_CASE` are the parameters to be supplied by you.
+ e.g. in `add_supplier n/NAME`, you fill in the `NAME` parameter with the supplier name (i.e. `add_supplier 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`. + e.g. `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. -* Items with `…`​ after them can be used multiple times including zero times.
- e.g. `[t/TAG]…​` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc. +* Items with `…​` after them can be used any number of times within the command.
+ e.g. `[t/TAG]…​` can be used as ` ` (omitted), `t/flour`, `t/flour t/sugar` etc. * Parameters can be in any order.
e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable. -* 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.
+* If a parameter is expected only once in the command, but you specified it multiple times, only the **last** occurrence of the parameter will be taken.
e.g. if you specify `p/12341234 p/56785678`, only `p/56785678` will be taken. -* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
+* Extraneous parameters will be ignored.
e.g. if the command specifies `help 123`, it will be interpreted as `help`.
-### Viewing help : `help` +
+ +
+ +:bulb: **Tip** + +* General format of commands:
+
+ <command> + + <index> + + <prefix/paramater> <prefix/paramater> ... +
+ + * Index depends on the command. + * Number of parameters depend on the command. + +* All command keywords have shortcut alternatives. They are the command type and the first letter of the information type.
+ For example:
+ * `add_s` is the same as `add_supplier` + * `edit_o` is the same as `edit_order` + * `list_t` is the same as `list_task` + +* You can find out the constraints for the parameters with their corresponding prefix [here](#61-prefix-summary). + +
+ +## 2.0 Checking for duplicates -Shows a message explaning how to access the help page. +Duplicate data are not allowed. Hence, adding of duplicate data or editing of an existing data such that it matches +with another existing data in the data list will not be successful. Here's how we check for duplicates: -![help message](images/helpMessage.png) +| Any two | are considered to be duplicates if | +|------------|-------------------------------------------------------------------------------| +| Suppliers | they have the same phone numbers | +| Orders | all the parameters (excluding `STATUS`) are the same (with matching cases). | +| Tasks | they have the same task names (with matching cases) and deadlines. | +| Menu items | they have the same item names (with matching cases). | -Format: `help` +
+## 2.1 Add -### Adding a person: `add` +Adding of an information to the specific list. Below are the specific add commands for supplier, order, task and menu item. -Adds a person to the address book. +
-Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +:pencil2: **Note**
+Adding of duplicate data is not allowed. Refer to this [table](#20-checking-for-duplicates) to see how duplicates are checked for. -
:bulb: **Tip:** -A person can have any number of tags (including 0)
-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` +### 2.1.1 Adding a supplier: `add_supplier` + +Adds a supplier to the list of suppliers. + +Syntax: `add_supplier n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` -### Listing all persons : `list` +
-Shows a list of all persons in the address book. +:clipboard: **Example 1: Standard command without tags** -Format: `list` +* `add_supplier n/John Doe p/98765432 e/johnd@example.com a/John Street`
+ Adds a supplier with the following details: + * Supplier Name: `John Doe` + * Phone Number: `98765432` + * Email: `johnd@example.com` + * Address: `John Street` + * Tags: Empty -### Editing a person : `edit` +:clipboard: **Example 2: Shortcut command with tags** -Edits an existing person in the address book. +* `add_s n/Betsy Cow t/diary e/betsycow@example.com a/Betsy Street p/12345678 t/meat`
+ Adds a supplier with the following details: + * Supplier Name: `Betsy Cow` + * Phone Number: `12345678` + * Email: `betsycow@example.com` + * Address: `Betsy Street` + * Tags: `Diary`, `Meat` -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +
-* 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. +### 2.1.2 Adding an order: `add_order` -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. +Adds an order into the list of orders. -### Locating persons by name: `find` +Syntax: `add_order on/ORDER_ITEM q/QUANTITY d/DEADLINE n/CUSTOMER_NAME a/CUSTOMER_ADDRESS p/CUSTOMER_PHONE_NUMBER [s/STATUS]` -Finds persons whose names contain any of the given keywords. +
-Format: `find KEYWORD [MORE_KEYWORDS]` +:information_source: **Information** -* 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` +* Statuses available for setting are + * `N` or `n` for `Not Delivered` + * `I` or `i` for `In Progress` + * `D` or `d` for `Delivered` +* If no status is provided, it is defaulted to `Not Delivered`. -Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +* All `ORDER_ITEM` **must** match (case-sensitive) a menu item in the menu. (e.g. `choclate` and `Chocolate` are considered different items).
+ i.e. If `ORDER_ITEM` does not match any of the existing menu items, you need to add it as a menu item first.
+ You can find out how to add a menu item [here](#214-adding-a-menu-item-add_item) -### Deleting a person : `delete` +
-Deletes the specified person from the address book. +
-Format: `delete INDEX` +
-* 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, …​ +:clipboard: **Example 1: Standard command without status** -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. +* `add_order on/Cupcake q/5 d/01/12/2024 n/John Cat p/91234567 a/John Street`
+ Adds an order with the following details: + * Order Item: `Cupcake` + * Quantity: `5` + * Deadline: `01/12/2024` + * Name: `John Cat` + * Phone Number: `91234567` + * Address: `John Street` + * Status: `Not Delivered` (Default) -### Clearing all entries : `clear` +:clipboard: **Example 2: Shortcut command with status** -Clears all entries from the address book. +* `add_o on/Chocolate Cookies q/10 d/02/10/2024 n/Dolly Sheep p/91827364 a/Dolly Street s/I`
+ Adds a supplier with the following details: + * Order Item: `Chocolate Cookies` + * Quantity: `10` + * Deadline: `02/10/2024` + * Name: `Dolly Sheep` + * Phone Number: `91827364` + * Address: `Dolly Street` + * Status: `In Progress` -Format: `clear` +
-### Exiting the program : `exit` +
-Exits the program. +### 2.1.3 Adding a task: `add_task` -Format: `exit` +Adds a task to the list of tasks. -### Saving the data +Syntax: `add_task n/TASK_NAME d/DEADLINE [s/STATUS]` -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +
-### Editing the data file +:information_source: **Information** -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. +* Statuses available for setting are + * `N` or `n` for `Not Done` + * `D` or `d` for `Done` +* If no status is provided, it is **defaulted** to `Not Done`. -
: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.
-### Archiving data files `[coming in v2.0]` +
-_Details coming soon ..._ +:clipboard: **Example 1: Standard command without status** --------------------------------------------------------------------------------------------------------------------- +* `add_task n/Buy cookie cutter d/22/12/2024`
+ Adds a task with the following details: + * Task Name: `Buy cookie cutter` + * Deadline: `22/12/2024` + * Status: `Not Done` (Default) -## FAQ +:clipboard: **Example 2: Shortcut command with status** -**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. +* `add_t n/Buy a card d/23/12/2024 s/D`
+ Adds a task with the following details: + * Task Name: `Buy a card` + * Deadline: `23/12/2024` + * Status: `Done` + +
+ +
+ +### 2.1.4 Adding a menu item: `add_item` + +Adds a menu item to the menu. + +Syntax: `add_item n/ITEM_NAME pr/PRICE c/COST` + +
+ +:information_source: **Information** + +* When entering price and cost values, you may choose to + 1. input **positive** integer numbers + 2. input **positive** decimal numbers (up to 2 decimal point). For example:
+ :heavy_check_mark: `5.1` or `5.03`
+ :x: `5.034` + +* The item can have a positive or negative profit (Profit = Price - Cost). Specifically: + * Negative profit margin means you are making a **loss** + * Positive profit margin means you are making a **profit** +* Note that profit calculated is only accurate when price and cost each have at most 16 digits. + * For example: 9283212222332323 and 92832122223323.55 are both 16 digits. +* You cannot add an item if there is another item that has the same name in the menu. The item name is case-sensitive. + * For example, chocolate and Chocolate are considered as different item. + +
+ +
+ +:clipboard: **Example 1: Standard command with integer values** + +* `add_item n/Chocolate cake pr/5 c/2`
+ Adds an order item with the following details: + * Item Name: `Chocolate cake` + * Price: `5` + * Cost: `2` + +:clipboard: **Example 2: Shortcut command with decimal values** + +* `add_i n/Limited Time Strawberry Cake pr/20.55 c/25.2`
+ Adds an order item with the following details: + * Item Name: `Limited Time Strawberry Cake` + * Price: `20.55` + * Cost: `25.2` + +
+ +## 2.2 Edit + +Editing of an information in a specific list. Below are the specific edit commands for supplier, order, task and menu item. + +
+ +:information_source: **Information** + +* Edits the information at the specified `INDEX`. +* The `INDEX` **must be a positive integer** 1, 2, 3, …​ +* All existing values will be replaced with the new values given. +* At least one of the fields (e.g. `n/NAME`) must be specified. +* Editing of a data such that it matches with another existing data is not allowed. Check [this table](#20-checking-for-duplicates) to see how duplicates are checked for. + +
+ +
+ +:exclamation: **Danger**

+ +Editing of information is one-way (i.e. You cannot revert your command).
+ +We are working to have an undo feature in the future. Sorry for the inconvenience caused. + +
+ +### 2.2.1 Editing a supplier: `edit_supplier` + +Edits an existing supplier’s information. + +Syntax: `edit_supplier INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` + +
+ +:information_source: **Information** + +* When editing tags: + * All existing tags will be replaced with the given tag (editing of tags is not accumulative). + * Typing `t/` removes all the existing tags. + +
+ +
+ +
+ +:clipboard: **Example 1: Standard command removing tags** + +* `edit_supplier 1 n/Johnny p/90138482 t/`
+ Edits the **first** supplier information (shown on screen) with the following details: + * Supplier Name: `Johnny` + * Phone Number: `90138482` + * Tags: Empty + +:clipboard: **Example 2: Shortcut command replacing tags** + +* `edit_s 3 t/flour e/mark@example.com`
+ Edits the **third** supplier information (shown on screen) with the following details: + * Email: `mark@example.com` + * Tags: `Supplies Flour` + +
+ +### 2.2.2 Editing an order: `edit_order` + +Edits an existing order’s information. + +Syntax: `edit_order INDEX [on/ORDER_ITEM] [q/QUANTITY] [d/DEADLINE] [n/CUSTOMER_NAME] [a/CUSTOMER_ADDRESS] [p/CUSTOMER_PHONE_NUMBER] [s/STATUS]` + +
+ +:information_source: **Information** + +* If a menu item is no longer your menu (i.e. you have deleted the menu item), the order details cannot be edited. + +* Statuses available for setting are + * `N` or `n` for `Not Delivered` + * `I` or `i` for `In Progress` + * `D` or `d` for `Delivered` +* When editing the order name, the input for order name is case-sensitive. + * For example, if menu item you want edit order name to "chocolate", you should input `edit_o 1 on/chocolate` and not `edit_o 1 on/ChOcolAte` + +
+ +
+ +
+ +:clipboard: **Example 1: Standard command** + +* `edit_order 1 p/91234567 d/05/05/2024`
+ Edits the **first** order information (shown on screen) with the following details: + * Customer Phone Number: `91234567` + * Deadline: `05/05/2024` + +:clipboard: **Example 2: Shortcut command updating status** + +* `edit_o 3 q/20 s/I`
+ Edits the **third** order information (shown on screen) with the following details: + * Quantity: `3` + * Status: `In Progress` + +
+ +### 2.2.3 Editing a task: `edit_task` + +Edits an existing task’s information. + +Syntax: `edit_task INDEX [n/TASK_NAME] [d/DEADLINE] [s/STATUS]` + +
+ +:information_source: **Information** + +* Statuses available for setting are + * `N` or `n` for `Not Done` + * `D` or `d` for `Done` + +
+ +
+ +:clipboard: **Example 1: Standard command** + +* `edit_task 1 n/Get creamer`
+ Edits the **first** task information (shown on screen) with the following details: + * Task Name: `Get creamer` + +:clipboard: **Example 2: Shortcut command updating status** + +* `edit_t 3 d/31/12/2024 s/N`
+ Edits the **third** task information (shown on screen) with the following details: + * Deadline: `31/12/2024` + * Status: `Not Done` + +
+ +### 2.2.4 Editing a menu item: `edit_item` + +Edits an existing menu item’s information. + +Syntax: `edit_item INDEX [n/ITEM_NAME] [pr/PRICE] [c/COST]` + +
+ +:information_source: **Information** + +* Editing a menu item does not invalidate or update the orders with the same order item name. +* Editing a menu item does not update and change the total revenue and profits on the home tab. + +* When entering price and cost values, you may choose to + 1. input **positive** integer numbers + 2. input **positive** decimal numbers (up to 2 decimal point). For example:
+ :heavy_check_mark: `5.1` or `5.03`
+ :x: `5.034` + +* The item can have a positive or negative profit margin (Profit Margin = Price - Cost). Specifically: + * Negative profit margin means you are making a **loss** + * Positive profit margin means you are making a **profit** +* Note that profit calculated is only accurate when price and cost each have at most 16 digits. + * For example: 9283212222332323 and 92832122223323.55 are both 16 digits. + +
+ +
+ +:clipboard: **Example 1: Standard command with integer values** + +* `edit_item 1 n/Coffee pr/2 c/1`
+ Edits the **first** menu item information with the following details: + * Item Name: `Coffee` + * Price: `2` + * Cost: `1` + +:clipboard: **Example 2: Shortcut command with decimal values** + +* `edit_i 3 d/Signature burger pr/8.5 c/2.25`
+ Edits the **third** menu item information with the following details: + * Item Name: `Signature burger` + * Price: `8.5` + * Cost: `2.25` + +
+ +## 2.3 Find + +Finding of specific information with the provided parameters. Below are the specific find commands for supplier, order, task and menu item. + +### 2.3.1 Finding a supplier: `find_supplier` + +Finds suppliers whose information matches with any of the given parameters. + +Syntax: `find_supplier NAME` + +
+ +:information_source: **Information** + +* Search is case-insensitive (e.g. `mark` will match with `Mark`). +* Order of the keywords does not matter (e.g. `Mark Lee` will match with `Lee Mark`). +* Only full words will match (e.g. `Mar` will not match with `Mark`). +* Suppliers matching with at least one keyword will be returned (i.e. `OR` search).
+ (e.g. `Mark Lee` will return `Mark Tan`, `Lee Chan`) + +
+ +
+ +
+ +:clipboard: **Example 1: Standard command** + +* `find_supplier Prima Flour`
+ Finds the supplier with the following details: + * Supplier Name contains either `Prima` or `Flour`.
+ + :heavy_check_mark: Example of suppliers that match: + * `Prima` + * `Soon Flour Pte Ltd` + * `PRIMA CONFECTIONARY` + + :x: Example of suppliers that do not match: + * `Prim` + * `PrimaFlour` + +:clipboard: **Example 2: Shortcut command** + +* `find_s Pte`
+ Finds the supplier with the following details: + * Supplier Name contains `Pte`. + + :heavy_check_mark: Example of suppliers that match: + * `Soon Flour Pte Ltd` + * `Tech Leong PTE LTD` + + :x: Example of suppliers that do not match: + * `Private` + * `Phoon Huat PteLtd` + +
+ +### 2.3.2 Finding an order: `find_order` + +Find orders whose information matches with any of the given parameters. + +Syntax: `find_order [on/ORDER_ITEM] [q/QUANTITY] [d/DEADLINE] [n/CUSTOMER_NAME] [a/CUSTOMER_ADDRESS] [p/CUSTOMER_PHONE_NUMBER] [s/STATUS]` + +
+ +
+ +:information_source: **Information** + +* At least one of the optional fields must be keyed in.
+ +* For `ORDER_ITEM` and `CUSTOMER_NAME`: + * Search is case-insensitive (e.g. `cookie` will match with `Cookie`). + * Order of the keywords does not matter (e.g. `Chocolate Cookie` will match with `Cookie Chocolate`). + * Only full words will match (e.g. `Chocolate` will not match with `Choco`). + * Orders matching with at least one keyword will be returned (i.e. `OR` search).
+ (e.g. `Chocolate Cake` will return `Chocolate Cookie`, `Strawberry Cake`).
+ +* For `STATUS`: + * Search is case-insensitive. + * Search + * `D` or `d` for `Delivered` + * `I` or `i` for `In Progress` + * `N` or `n` for `Not delivered`. + * e.g. `find_o s/D` will return orders marked as `Delivered`.
+ +* For `CUSTOMER_ADDRESS`: + * Search is case-sensitive. + (e.g. The search for orders with customer address `smith` will match with `smith` but not with `Smith`). + * Order of the keywords matter (e.g. `Amy Lee` will not match with `Lee Amy`). + * Only full words will match. + * Order's respective parameter must fully match with the given parameter.
+ (e.g. `Amy Lee` will only match with `Amy Lee` and not with `Amy Lee Tan`). + +
+ +
+ +
+ +:clipboard: **Example 1: Standard command with single fields** + +* `find_order on/Chocolate Cake`
+ Finds the order with the following details: + * Order Item contains `Chocolate` or `Cake`
+ + :heavy_check_mark: Example of orders that match: + * `Chocolate Cookie` + * `Chocolate Cake` + * `CAKE WITH VANILLA` + + :x: Example of orders that do not match: + * `Choco` + * `ChocolatePie` + +:clipboard: **Example 2: Shortcut command with multiple fields** + +* `find_o on/Cake d/01/01/2023`
+ Finds the order with the following details: + * Supplier Name contains `Cake` + * Deadline is on `01/01/2023`
+ + :heavy_check_mark: Example of orders that match: + * `on/Chocolate Cake d/01/01/2023` + * `on/CAKE WITH VANILLA AND CHOCOLATE d/01/01/2023` + + :x: Example of orders that do not match: + * `on/Chocolate Cake d/03/03/2024` + * `on/VANILLA CAKES d/01/01/2023` + +
+ +### 2.3.3 Finding a task: `find_task` + +Finds tasks with information that matches with any of the given parameters. + +Syntax: `find_task [n/TASK_NAME] [d/DEADLINE] [s/STATUS]` + +
+ +
+ +:information_source: **Information** + +* At least one of the optional fields must be keyed in.
+ +* For `TASK_NAME`: + * Search is case-insensitive (e.g. `sugar` will match with `Sugar`). + * Order of the keywords does not matter (e.g. `Flour Sugar` will match with `Sugar Flour`). + * Only full words will match (e.g. `Sugar` will not match with `Sugars`). + * Tasks matching with at least one keyword will be returned (i.e. `OR` search).
+ (e.g. `Order Flour` will return `Order Sugar`, `Order 10kg Flour`).
+ +* For `STATUS`: + * Search is case-insensitive. + * Search + * `D` or `d` for `Done` + * `N` or `n` for `Not delivered`. + * e.g. `find_o s/D` will return tasks marked as `Done`.
+ +
+ +
+ +:clipboard: **Example 1: Standard command with single fields** + +* `find_task s/N`
+ Finds all task that are `Not Done`. + +:clipboard: **Example 2: Shortcut command with multiple fields** + +* `find_t n/Buy eggs d/17/02/2023`
+ Finds the order with the following details: + * Task Name contains `Buy eggs`. + * Deadline is on `17/02/2023` + + :heavy_check_mark: Example of tasks that match: + * `n/Buy eggs d/17/02/2023` + * `n/Buy flour d/17/02/2023` + + :x: Example of tasks that do not match: + * `n/Make eggcream d/17/02/2023` + * `n/Buy flour d/30/03/2024` + +
+ +
+ +### 2.3.4 Finding a menu item: `find_item` + +Find tasks with information that matches with any of the given parameters. + +Syntax: `find_item ITEM_NAME` + +
+ +:information_source: **Information** + +* Search is case-insensitive (e.g. `cookie` will match with `Cookie`). +* Order of the keywords does not matter (e.g. `Chocolate Cookie` will match with `Cookie Chocolate`). +* Only full words will match (e.g. `Chocolate` will not match with `Choco`). +* Menu items matching with at least one keyword will be returned (i.e. `OR` search).
+ (e.g. `Chocolate Cake` will return `Chocolate Cookie`, `Strawberry Cake`) + +
+ +
+ +:clipboard: **Example 1: Standard command** + +* `find_item vanilla cupcake`
+ Finds the menu item with the following details: + * Item Name contains either `vanilla` or `cake`.
+ + :heavy_check_mark: Example of order items that match: + * `Strawberry cupcake` + * `CAKE WITH VANILLA` + + :x: Example of order item that do not match: + * `chocolate cake` + +:clipboard: **Example 2: Shortcut command** + +* `find_i Cake`
+ Finds the menu item with the following details: + * Item Name contains `Cake`. + + :heavy_check_mark: Example of order items that match: + * `Strawberry Cake` + * `CAKE WITH VANILLA` + + :x: Example of order item that do not match: + * `Vanilla Cupcake` + +
+ +
+ +## 2.4 Delete + +Deleting of an information to the specific list. Below are the specific delete commands for supplier, order, task and menu item. + +
+ +:information_source: **Information** + +* Deletes the information at the specified `INDEX`. +* The `INDEX` **must be a positive integer** 1, 2, 3, …​ +* All values at the `INDEX` will be deleted. + +
+ +
+ +:exclamation: **Danger**

+ +Deleting of information is one-way (i.e. You cannot revert your command).
+ +We are working to have an undo feature in the future. Sorry for the inconvenience caused. + +
+ +### 2.4.1 Deleting a supplier: `delete_supplier` + +Deletes the specified supplier from the contact list. + +Syntax: `delete_supplier INDEX` + +
+ +:clipboard: **Example 1: Standard command** + +* `delete_supplier 2`
+ Deletes the **second** supplier from the visible contact list + +:clipboard: **Example 2: Shortcut command after using `find_supplier`** + +* `find_supplier John` followed by `delete_s 1`
+ * `find_supplier John` lists all suppliers with names that contain `John` + * `delete_s 1` deletes the **first** supplier with a name that contains `John` from the visible contact list + +
+ +
+ +### 2.4.2 Deleting an order: `delete_order` + +Deletes the specified order from the order list. + +Syntax: `delete_order INDEX` + +
+ +:clipboard: **Example 1: Standard command** + +* `delete_order 2`
+ Deletes the **second** order from the visible order list + +:clipboard: **Example 2: Shortcut command after using `find_order`** + +* `find_order on/Cake` followed by `delete_o 1`
+ * `find_order on/Cake` lists all orders with item names that contain `Cake` + * `delete_o 1` deletes the **first** order with an item name that contains `Cake` from the visible order list + +
+ +### 2.4.3 Deleting a task: `delete_task` + +Deletes the specified task from the task list. + +Syntax: `delete_task INDEX` + +
+ +:clipboard: **Example 1: Standard command** + +* `delete_task 2`
+ Deletes the **second** task from the visible task list + +:clipboard: **Example 2: Shortcut command after using `find_task`** + +* `find_task n/flour` followed by `delete_t 1`
+ * `find_task n/flour` lists all tasks with task names that contain `flour` + * `delete_t 1` deletes the **first** task with a task name that contains `flour` from the visible task list + +
+ +
+ +### 2.4.4 Deleting a menu item: `delete_item` + +Deletes the specified menu item from the menu. + +Syntax: `delete_item INDEX` + +
+ +:information_source: **Information** + +Deleting a menu item does not invalidate the orders with the same order item name. + +
+ +
+ +:clipboard: **Example 1: Standard command** + +* `delete_menu 2`
+ Deletes the **second** menu item from the visible menu + +:clipboard: **Example 2: Shortcut command after using `find_item`** + +* `find_item cupcake` followed by `delete_m 1`
+ * `find_item cupcake` lists all menu items with item names containing `cupcake` + * `delete_m 1` deletes the **first** menu item with an item name that contains `cupcake` from the visible menu + +
+ +
+ +## 2.5 Sort + +Sorting of information in the specific list. Below are the specific sort commands for task and order. + +
+ +:information_source: **Information** + +* Sorts all the data in the specific list according to a criteria. +* Criteria is case-insensitive (i.e. `time_added`, `TIME_ADDED` `Time_Added` are all valid). +* The default criteria (when no criteria is specified) is `Status_and_deadline`. + +* Criteria available are: + * `Time_added` + * Sorts data in ascending order. + * Data added **first** is at the **top** while data added the **latest** is at the **bottom**. + * `Deadline` + * Sorts data in ascending order. + * Data with the **earliest** deadline is at the **top** while those with the **latest** deadline is at the **bottom**. + * `Status` + * Refer to the specific command explanation below. + * `Name` + * Sorts data in lexicographical order (ignoring case) with respect to the name. + * Data with name starting with "**a**" will be placed **above** a data with name starting with "**B**". + * `Status_and_deadline` (Default) + * Sorts all data by their status first, followed by their deadline + (while preserving the relative ordering from the first sort, i.e. Stable sort) + +
+ +### 2.5.1 Sorting a task: `sort_task` + +Sorts tasks according to the criteria specified. + +Syntax: `sort_task [c/CRITERIA]` + +
+ +
+ +:information_source: **Information** + +* `Status` - Sorted in this order from top to bottom: + * `Not Done` + * `Done`

+* `Status_and_deadline` - Sorts all data by their status first, followed by their deadline
+ Specifically in this order from top to bottom: + * `Not Done` & `Earliest Deadline` + * `Not Done` & `Latest Deadline` + * `Done` & `Earliest Deadline` + * `Done` & `Latest Deadline` + +
+ +
+ +:clipboard: **Example 1: Standard command with default parameters** + +* `sort_task`
+ Sorts all tasks according to their `Status_and_deadline` + +:clipboard: **Example 2: Shortcut command with parameters** + +* `sort_t c/deadline`
+ Sorts all tasks according to their `deadline` + +:clipboard: **Example 3: Shortcut command after using `find_task`** + +* `find_task s/N` followed by `sort_t c/Deadline`
+ * `find_task s/N` first lists all tasks with status as `Not Done` + * `sort_t c/deadline` sorts all `Not Done` tasks found according to their `Deadline` + +
+ +### 2.5.2 Sorting a order: `sort_order` + +Sorts orders according to the criteria specified. + +Syntax: `sort_order [c/CRITERIA]` + +
+ +
+ +:information_source: **Information** + +* `Status` - Sorted in this order from top to bottom: + * `Not Delivered` + * `In Progress` + * `Delivered`

+* `Status_and_deadline` - Sorts all data by their status first, followed by their deadline
+ Specifically in this order from top to bottom: + * `Not Delivered` & `Earliest Deadline` + * `Not Delivered` & `Latest Deadline` + * `In Progress` & `Earliest Deadline` + * `In Progress` & `Latest Deadline` + * `Delivered` & `Earliest Deadline` + * `Delivered` & `Latest Deadline` + +
+ +
+ +:clipboard: **Example 1: Standard command with default parameters** + +* `sort_order`
+ Sorts all orders according to their `Status_and_deadline` + +:clipboard: **Example 2: Shortcut command with parameters** + +* `sort_o c/Status`
+ Sorts all orders according to their `Status` + +:clipboard: **Example 3: Shortcut command after using `find_order`** + +* `find_order s/N` followed by `sort_o c/Name`
+ * `find_order s/N` lists all orders with status as `Not Delievered` + * `sort_o c/Name` sorts all `Not Delivered` orders found according to their `Name` + +
+ +
+ +## 2.6 List + +Shows all data in the specific list. Below are the specific list commands for suppliers, order, tasks and menu. + +### 2.6.1 Listing all suppliers: `list_supplier` + +Shows a list of all suppliers in the contact list. + +Syntax: `list_supplier` + +
+ +:clipboard: **Example 1: Standard command** + +* `list_supplier`
+ Shows all suppliers. + +:clipboard: **Example 2: Shortcut command with parameters** + +* `list_s n/John`
+ Shows all suppliers. + +
+ +### 2.6.2 Listing all orders: `list_order` + +Shows a list of all orders in the order list. + +Syntax: `list_order` + +
+ +:clipboard: **Example 1: Standard command** + +* `list_order`
+ Shows all orders. + +:clipboard: **Example 2: Shortcut command with parameters** + +* `list_o on/Straberry`
+ Shows all orders. + +
+ +### 2.6.3 Listing all tasks: `list_task` + +Shows a list of all tasks in the task list. + +Syntax: `list_task` + +
+ +:clipboard: **Example 1: Standard command** + +* `list_task`
+ Shows all tasks. + +:clipboard: **Example 2: Shortcut command with parameters** + +* `list_t s/N`
+ Shows all tasks. + +
+ +### 2.6.4 Listing all menu items: `list_menu` + +Shows a list of all menu items in the menu. + +Syntax: `list_menu` + +
+ +:clipboard: **Example 1: Standard command** + +* `list_menu`
+ Shows all menu items. + +:clipboard: **Example 2: Shortcut command with parameters** + +* `list_m price`
+ Shows all menu items. + +
+ +
+ +## 2.7 Clear + +Clears all data in the specific list. Below are the specific clear commands for suppliers, order, tasks and menu. + +
+ +:exclamation: **Danger**

+ +Clearing of data is one-way (i.e. You cannot revert your command).
+ +We are working to have an undo feature in the future. Sorry for the inconvenience caused. + +
+ +### 2.7.1 Clear all supplier entries: `clear_supplier` + +Clears all supplier entries from the contact list. + +Syntax: `clear_supplier` + +
+ +:clipboard: **Example 1: Standard command** + +* `clear_supplier`
+ Clears all supplier data from contact list. + +:clipboard: **Example 2: Shortcut command with parameters** + +* `clear_s n/Name`
+ Clears all supplier data from contact list. + +
+ +### 2.7.2 Clear all order entries: `clear_order` + +Clears all order entries from the order list. + +Syntax: `clear_order` + +
+ +
+ +:clipboard: **Example 1: Standard command** + +* `clear_order`
+ Clears all order data from order list. + +:clipboard: **Example 2: Shortcut command with parameters** + +* `clear_o on/Chocolate`
+ Clears all order data from order list. + +
+ +### 2.7.3 Clear all task entries: `clear_task` + +Clears all task entries from the task list. + +Syntax: `clear_task` + +
+ +:clipboard: **Example 1: Standard command** + +* `clear_task`
+ Clears all task data from task list. + +:clipboard: **Example 2: Shortcut command with parameters** + +* `clear_t s/N`
+ Clears all task data from task list. + +
+ +### 2.7.4 Clear all menu item entries: `clear_menu` + +Clears all menu item entries from the menu. + +Syntax: `clear_menu` + +
+ +:information_source: **Information** + +Clearing all menu items does not invalidate the orders. + +
+ +
+ +
+ +:clipboard: **Example 1: Standard command** + +* `clear_menu`
+ Clears all menu item data from menu. + +:clipboard: **Example 2: Shortcut command with parameters** + +* `clear_m price`
+ Clears all menu item data from menu. + +
+ +## 2.8 Switch + +Allows for you to switch to the tab for view your respective information. You can use the GUI to navigate it as shown [here](#14-navigating-the-user-interface) or use the command shown here. + +### 2.8.1 Switching tabs: `tab` + +Switch to another tab. + +Syntax: `tab t/TAB` + +
+ +:information_source: **Information** + +* `TAB` needs to be in all-caps and is case-sensitive +* The available tabs are `HOME`, `ORDERS`, `CONTACTS`, `MENU`, `TASKS` + +
+ +
+ +:clipboard: **Example 1: Standard command** + +* `tab t/HOME`
+ Navigates you to the home tab + +
+ +
+ +## 2.9 Others + +Here are other functionalities and commands that are available for you to utilise. + +### 2.9.1 Viewing help: `help` + +Shows a message with a link to the help page (this page). + +

+ +
Figure 2.9.1.1: Help Command Message +

+ +Syntax: `help` + +### 2.9.2 Uploading a csv file + +Uploading of a valid csv file into Trackr allows Trackr to parse the data to `Task`, `Order`, `Supplier` and `Menu Items` and adds them to their respective lists. + +It is important to strictly follow the format specified below for your data to parse successfully. + +
+ +:information_source: **Information** + +**Step 1:** Start each row with the type of item you want to add (i.e. `Orders`) + +**Step 2:** List out on the next row all the respective item fields (i.e. `OrderName`, `Quantity`, ...) + +**Step 3:** Fill in the next few rows will all the corresponding data, without leaving any fields empty (i.e. `Chocolate Cookies`, `10`, ...) + +Note: For any **optional** fields, you can indicate it with a `-` to have it be taken as its default value. + +
+ +

+ +
Figure 2.9.2.1: Example of a valid csv file +

+ +### 2.9.3 Exiting the program: `exit` + +Exits the program. + +Syntax: `exit` + +# 3 Data + +This section will inform you how the data is saved. + +## 3.1 Saving of data + +All data are saved after any command is executed successfully. There is no need for you to manually save. + +
+ +## 3.2 Editing the data file + +Trackr data are saved as a JSON file at `[JAR file location]/data/trackr.json`. + +Advanced users are welcome to update data directly by editing that data file. + +
+ +:warning: **Caution:**

+ +It is advised to make your changes through our application and not through editing the data file directly. + +Trackr does not have backup data if your edit corrupts the data. + +Please proceed with caution. + +
+ +
+ +:exclamation: **Danger**

+ +If your changes to the data file are invalid, Trackr will discard all data and start with an empty data file at the next run. + +
+ +Here is the layout of the data file: + +```json +{ + "suppliers": [], + "tasks": [], + "menuItems": [], + "orders": [] +} +``` + +Here are the respective formats for each of the data types: + +* Supplier
+ ```json + { + "name" : "Prima Flour", + "phone" : "87438807", + "email" : "sales.primaflour@prima.com.sg", + "address" : "Blk 30 Geylang Street 29, #06-40", + "tagged" : [ "flour" ] + } + ``` + +
+ +* Task
+ ```json + { + "taskName" : "Buy flour", + "taskDeadline" : "01/01/2024", + "taskStatus" : "N", + "timeAdded" : "2023-04-04T16:35:55.858687" + } + ``` +* Menu Item
+ ```json + { + "itemName" : "Chocolate Cookies", + "itemCost" : "1.20", + "itemPrice" : "5.00", + "itemProfit" : "3.80" + } + ``` +* Order
+ ```json + { + "customerName" : "Amy", + "customerPhone" : "12345678", + "customerAddress" : "123 Smith Street", + "menuItem" : { + "itemName" : "Chocolate Cookies", + "itemCost" : "1.20", + "itemPrice" : "5.00", + "itemProfit" : "3.80" + }, + "orderDeadline" : "01/01/2024", + "orderQuantity" : "2", + "orderStatus" : "N", + "timeAdded" : "2023-04-04T16:35:55.864688500" + } + ``` + +# 4 Upcoming features `[coming soon]` + +* Highlight overdue orders. +* Better representation of sales report (e.g. Pie charts or graphs) to track your business’s growth. +* Ability to export your data as csv. + +-------------------------------------------------------------------------------------------------------------------- + +# 5 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 Trackr home folder. + +**Q**: Can I search for `suppliers` by other fields such as `tags`?
+**A**: Currently it is not supported but will be added in the future. You can only find `suppliers` by their `names`. Sorry for the inconvenience caused. + +**Q**: Can I edit tags present in `suppliers`?
+**A**: This is not supported currently. You can just replace the tags directly using the [`edit_supplier`](#221-editing-a-supplier-edit_supplier) command. We will consider adding this as a feature in the future. + +**Q**: Do I require internet access to use Trackr?
+**A**: No, you can use Trackr without any internet access. -------------------------------------------------------------------------------------------------------------------- -## 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` +
+ +# 6 Summary + +# 6.1 Prefix Summary + +| Parameter | Prefix | Rules | +|--------------------------------------------|--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Name (Task, Supplier, Customer, Menu Item) | n/ | - Should only contains alphanumeric characters and spaces | +| Phone Number (Supplier, Customer) | p/ | - Should only contain digits
- Should have least 3 digits | +| Email | e/ | - Should only be of the form `local@domain` and only accept alphanumeric characters
- `local` allows for special characters `+`, `_`, `.` and `-` as well.
- `domain` must be at least 2 letters long.
- `.com` is not required. | +| Address (Supplier, Customer) | a/ | - Can contain any letters | +| Tag | t/ | - Should only contain alphanumeric characters
- Should not contain spaces | +| Deadline (Task, Order) | d/ | - Should be of the format `dd/MM/yyyy` - Dates that have passed are allowed | +| Status (Task, Order) | s/ | - Should only contain alphanumeric characters | +| Order Name | on/ | - Should only contain alphanumeric characters
- Order name should be a valid menu item name | +| Order Quantity | q/ | - Should only contain positive integer values | +| Price | pr/ | - Should only contain positive numeric values
- Allows for integers or values up to 2 decimal places | +| Cost | c/ | - Should only contain positive numeric values
- Allows for integers or values up to 2 decimal places | +| Tab | t/ | - Should only contain alphabetical characters
- Should be in all caps | +| Criteria | c/ | - Should only contain alphabetical characters
| + +## 6.2 Command Summary + +| Action | Format, Examples | +|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Add** | `add_supplier` / `add_s`
e.g., `add_s n/Betsy Cow t/diary e/betsycow@example.com a/Betsy Street p/12345678 t/meat`

`add_order` / `add_o`
e.g., `add_o n/John Doe l/John Street d/12/12/2023 q/10 on/Cupcakes`

`add_task` / `add_t`
e.g., `add_t d/Buy a card d/23/12/2024 s/D`

`add_item` / `add_i`
e.g., `add_i n/Chocolate Cake pr/30 c/10` | +| **Edit** | `edit_supplier` / `edit_s`
e.g., `edit_s 3 t/flour e/mark@example.com`

`edit_order` / `edit_o`
e.g., `edit_o 3 q/20`

`edit_task` / `edit_t`
e.g., `edit_t 1 s/D`

`edit_item` / `edit_i`
e.g., `edit_i n/Chocolate Chip Cookie` | +| **Delete** | `delete_supplier` / `delete_s`
e.g., `delete_s 2`

`delete_order` / `delete_o`
e.g., `delete_o 1`

`delete_task` / `delete_t`
e.g., `delete_t 4`

`delete_item` / `delete_i`
e.g., `delete_i 2` | +| **Find** | `find_supplier` / `find_s`
e.g., `find_s Pte`

`find_order` / `find_o`
e.g., `find_order on/Chocolate`

`find_task` / `find_t`
e.g., `find_t s/N`

`find_item` / `find_i`
e.g., `find_i Chocolate` | +| **Sort** | `sort_task` / `sort_t`
e.g., `sort_t c/Deadline`

`sort_order` / `sort_o`
e.g., `sort_order c/Deadline` | +| **List** | `list_supplier` / `list_s`

`list_order` / `list_o`

`list_task` / `list_t`

`list_menu` / `list_m` | +| **Clear** | `clear_supplier` / `clear_s`

`clear_order` / `clear_o`

`clear_task` / `clear_t`

`clear_menu` / `clear_m` | +| **Tab** | `tab`
e.g., `tab t/Home` | +| **Help** | `help` | +| **Exit** | `exit` | diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..673af829067 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "Trackr" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2223S2-CS2103T-W15-2/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..b66d018386e 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: "Trackr"; font-size: 32px; } } diff --git a/docs/_sass/minima/_layout.scss b/docs/_sass/minima/_layout.scss index ca99f981701..885a4128917 100644 --- a/docs/_sass/minima/_layout.scss +++ b/docs/_sass/minima/_layout.scss @@ -179,6 +179,14 @@ h1, h2, h3 { margin-top: $spacing-unit * 2 } h4, h5, h6 { margin-top: $spacing-unit } + h1 { + @include relative-font-size(2); + + @media screen and (min-width: $on-large) { + @include relative-font-size(2.25); + } + } + h2 { @include relative-font-size(1.75); diff --git a/docs/_sass/minima/custom-styles.scss b/docs/_sass/minima/custom-styles.scss index 56b5d56b430..10fae147341 100644 --- a/docs/_sass/minima/custom-styles.scss +++ b/docs/_sass/minima/custom-styles.scss @@ -1,6 +1,6 @@ // Placeholder to allow defining custom styles that override everything else. // (Use `_sass/minima/custom-variables.scss` to override variable defaults) -h2, h3, h4, h5, h6 { +h1, h2, h3, h4, h5, h6 { color: #e46c0a; } diff --git a/docs/_sass/minima/custom-variables.scss b/docs/_sass/minima/custom-variables.scss index a128970cbe7..075b2a0b924 100644 --- a/docs/_sass/minima/custom-variables.scss +++ b/docs/_sass/minima/custom-variables.scss @@ -37,8 +37,10 @@ $theme-colors: ( "secondary": $secondary, "success": $success, "info": $info, + "tip": $primary, "warning": $warning, "danger": $danger, + "example": $purple, "light": $light, "dark": $dark ) !default; diff --git a/docs/diagrams/AddCommandActivityDiagram.puml b/docs/diagrams/AddCommandActivityDiagram.puml new file mode 100644 index 00000000000..a1110b198b4 --- /dev/null +++ b/docs/diagrams/AddCommandActivityDiagram.puml @@ -0,0 +1,29 @@ +@startuml + !include style.puml + + start + :User executes AddXYZCommand; + :AddXYZCommandParser extracts the relevant information + from the input argument; + + 'Since the beta syntax does not support placing the condition outside the + 'diamond we place it as the true branch instead. + + if () then ([Invalid Input Argument]) + :Display error message + on invalid command argument; + stop + else ([else]) + :Create XYZ object; + if() then ([List has Item]) + :Display error message + on duplicate item; + stop + else ([else]) + :Add XYZ to XYZ XYZList; + : Update XYZ in FilteredXYZList; + :Save XYZ to local data with internal XYZList; + endif + : Success message is returned; + stop + @enduml diff --git a/docs/diagrams/ArchitectureDiagram.puml b/docs/diagrams/ArchitectureDiagram.puml index 4c5cf58212e..47340c73df2 100644 --- a/docs/diagrams/ArchitectureDiagram.puml +++ b/docs/diagrams/ArchitectureDiagram.puml @@ -1,9 +1,10 @@ @startuml -!include !include !include !include style.puml +skinparam BorderColor #FFFFFF + Package " "<>{ Class UI UI_COLOR Class Logic LOGIC_COLOR diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index ef81d18c337..014ee6b5444 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 : "delete_supplier 1" activate ui UI_COLOR -ui -[UI_COLOR]> logic : execute("delete 1") +ui -[UI_COLOR]> logic : execute("delete_supplier 1") activate logic LOGIC_COLOR -logic -[LOGIC_COLOR]> model : deletePerson(p) +logic -[LOGIC_COLOR]> model : deleteItem(p, ModelEnum.SUPPLIER) activate model MODEL_COLOR model -[MODEL_COLOR]-> logic deactivate model -logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook) +logic -[LOGIC_COLOR]> storage : saveTrackr(supplierList, taskList, orderList) activate storage STORAGE_COLOR storage -[STORAGE_COLOR]> storage : Save to file diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml deleted file mode 100644 index 598474a5c82..00000000000 --- a/docs/diagrams/BetterModelClassDiagram.puml +++ /dev/null @@ -1,21 +0,0 @@ -@startuml -!include style.puml -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 - -UniqueTagList -right-> "*" Tag -UniquePersonList -right-> Person - -Person -up-> "*" Tag - -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -@enduml diff --git a/docs/diagrams/ClearCommandActivityDiagram.puml b/docs/diagrams/ClearCommandActivityDiagram.puml new file mode 100644 index 00000000000..8d3269151aa --- /dev/null +++ b/docs/diagrams/ClearCommandActivityDiagram.puml @@ -0,0 +1,15 @@ +@startuml +skinparam activityFontSize 20 +skinparam activityArrowFontSize 18 + +'Since the beta syntax does not support placing the condition outside the +'diamond we place it as the true branch instead. + +start +:User enters ClearXYZCommand; +:Trackr removes all XYZ items from XYZList; +:Save data into data file; +:Shows success message; +stop + +@enduml diff --git a/docs/diagrams/CommitActivityDiagram.puml b/docs/diagrams/CommitActivityDiagram.puml index 6a6b23a006f..5dba92b0fb2 100644 --- a/docs/diagrams/CommitActivityDiagram.puml +++ b/docs/diagrams/CommitActivityDiagram.puml @@ -7,8 +7,8 @@ start if () then ([command commits AddressBook]) :Purge redundant states; - :Save AddressBook to - addressBookStateList; + :Save Trackr to + trackrStateList; else ([else]) endif stop diff --git a/docs/diagrams/ComponentManagers.puml b/docs/diagrams/ComponentManagers.puml index 5e907dc1115..bf5321da97d 100644 --- a/docs/diagrams/ComponentManagers.puml +++ b/docs/diagrams/ComponentManagers.puml @@ -1,31 +1,29 @@ @startuml !include style.puml skinparam arrowThickness 1.1 -skinparam arrowColor LOGIC_COLOR_T4 -skinparam classBackgroundColor LOGIC_COLOR -package Logic { -Class "<>\nLogic" as Logic -Class LogicManager +package "Logic " LOGIC_COLOR_T1 { +Class "<>\nLogic" as Logic LOGIC_COLOR +Class LogicManager LOGIC_COLOR } -package Model{ -Class "<>\nModel" as Model -Class ModelManager +package "Model " MODEL_COLOR_T1 { +Class "<>\nModel" as Model MODEL_COLOR +Class ModelManager MODEL_COLOR } -package Storage{ -Class "<>\nStorage" as Storage -Class StorageManager +package "Storage " STORAGE_COLOR_T1 { +Class "<>\nStorage" as Storage STORAGE_COLOR +Class StorageManager STORAGE_COLOR } Class HiddenOutside #FFFFFF HiddenOutside ..> Logic -LogicManager .up.|> Logic -ModelManager .up.|> Model -StorageManager .up.|> Storage +LogicManager .[LOGIC_COLOR]up.|> Logic +ModelManager .[MODEL_COLOR]up.|> Model +StorageManager .[STORAGE_COLOR]up.|> Storage -LogicManager --> Model -LogicManager --> Storage +LogicManager -[LOGIC_COLOR]-> Model +LogicManager -[LOGIC_COLOR]-> Storage @enduml diff --git a/docs/diagrams/DeleteCommandActivityDiagram.puml b/docs/diagrams/DeleteCommandActivityDiagram.puml new file mode 100644 index 00000000000..ff193f9f2d3 --- /dev/null +++ b/docs/diagrams/DeleteCommandActivityDiagram.puml @@ -0,0 +1,28 @@ +@startuml +!include style.puml + +start +:User executes DeleteXYZCommand; +:DeleteXYZCommandParser extracts the relevant one-based index +from the input argument; + +'Since the beta syntax does not support placing the condition outside the +'diamond we place it as the true branch instead. + +if () then ([Invalid Index Argument]) + :Display error message + on invalid command argument; + stop +else ([else]) + :Get zero-based index from one-based index; + :Check out-of-bounds condition for index on FilteredXYZList; + if () then ([true]) + : Display invalid XYZ index error message; + stop + else ([false]) + : Retrieve item to delete from FilteredXYZList; + : Item is deleted from XYZList; + : XYZList with XYZ removed is saved to local storage; + : Success message is returned; + stop +@enduml diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 1dc2311b245..e1aebaf7c15 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -3,9 +3,9 @@ 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 ":TrackrParser" as TrackrParser LOGIC_COLOR +participant ":DeleteOrder\nCommandParser" as DeleteOrderCommandParser LOGIC_COLOR +participant "d:DeleteOrder\nCommand" as DeleteOrderCommand LOGIC_COLOR participant ":CommandResult" as CommandResult LOGIC_COLOR end box @@ -13,56 +13,56 @@ box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR end box -[-> LogicManager : execute("delete 1") +[-> LogicManager : execute("delete_order 1") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") -activate AddressBookParser +LogicManager -> TrackrParser : parseCommand("delete_order 1") +activate TrackrParser -create DeleteCommandParser -AddressBookParser -> DeleteCommandParser -activate DeleteCommandParser +create DeleteOrderCommandParser +TrackrParser -> DeleteOrderCommandParser +activate DeleteOrderCommandParser -DeleteCommandParser --> AddressBookParser -deactivate DeleteCommandParser +DeleteOrderCommandParser --> TrackrParser +deactivate DeleteOrderCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") -activate DeleteCommandParser +TrackrParser -> DeleteOrderCommandParser : parse("1") +activate DeleteOrderCommandParser -create DeleteCommand -DeleteCommandParser -> DeleteCommand -activate DeleteCommand +create DeleteOrderCommand +DeleteOrderCommandParser -> DeleteOrderCommand +activate DeleteOrderCommand -DeleteCommand --> DeleteCommandParser : d -deactivate DeleteCommand +DeleteOrderCommand --> DeleteOrderCommandParser : d +deactivate DeleteOrderCommand -DeleteCommandParser --> AddressBookParser : d -deactivate DeleteCommandParser +DeleteOrderCommandParser --> TrackrParser : d +deactivate DeleteOrderCommandParser 'Hidden arrow to position the destroy marker below the end of the activation bar. -DeleteCommandParser -[hidden]-> AddressBookParser -destroy DeleteCommandParser +DeleteOrderCommandParser -[hidden]-> TrackrParser +destroy DeleteOrderCommandParser -AddressBookParser --> LogicManager : d -deactivate AddressBookParser +TrackrParser --> LogicManager : d +deactivate TrackrParser -LogicManager -> DeleteCommand : execute() -activate DeleteCommand +LogicManager -> DeleteOrderCommand : execute() +activate DeleteOrderCommand -DeleteCommand -> Model : deletePerson(1) +DeleteOrderCommand -> Model : deleteItem(1,\nModelEnum.ORDER) activate Model -Model --> DeleteCommand +Model --> DeleteOrderCommand deactivate Model create CommandResult -DeleteCommand -> CommandResult +DeleteOrderCommand -> CommandResult activate CommandResult -CommandResult --> DeleteCommand +CommandResult --> DeleteOrderCommand deactivate CommandResult -DeleteCommand --> LogicManager : result -deactivate DeleteCommand +DeleteOrderCommand --> LogicManager : result +deactivate DeleteOrderCommand [<--LogicManager deactivate LogicManager diff --git a/docs/diagrams/EditCommandActivityDiagram.puml b/docs/diagrams/EditCommandActivityDiagram.puml new file mode 100644 index 00000000000..894434bce60 --- /dev/null +++ b/docs/diagrams/EditCommandActivityDiagram.puml @@ -0,0 +1,37 @@ +@startuml +skinparam activityFontSize 20 +skinparam activityArrowFontSize 18 + +'@@author HmuuMyatMoe-reused +'Reused from https://forum.plantuml.net/195/is-there-any-support-for-subactivity-or-the-rake-symbol +sprite $rake + + +'@@author + +start +:User enters command +to edit XYZ in XYZ List; +:EditXYZCommandParser + parses the user input; +if () then ([Invalid command format]) + :Display Invalid Command Format + error message; + stop +else ([else]) +:Change one-based index + to zero-based index; +:Check if index given is valid; + +if () then ([Index is out of bounds]) + :Display Invalid Item + Index error message; + stop +else ([else]) +:Edit item XYZ <$rake>; +stop + +@enduml diff --git a/docs/diagrams/EditItemXYZRakeActivityDiagram.puml b/docs/diagrams/EditItemXYZRakeActivityDiagram.puml new file mode 100644 index 00000000000..f556989cc63 --- /dev/null +++ b/docs/diagrams/EditItemXYZRakeActivityDiagram.puml @@ -0,0 +1,28 @@ +@startuml +skinparam activityFontSize 20 +skinparam activityArrowFontSize 18 +skinparam titleFontSize 25 + +title Activity: Edit item XYZ\n + +start +:Item XYZ at index (zero-based) +retrieved from its FilteredXYZList; +:Copy of the item XYZ is made and edited; + +if () then ( +[Edited item is considered to be the same as +other items in the list or same as original item] +) +:Display duplicate item error message; +stop + +else ([else] +) +:Item in XYZ List is edited; +:FilteredXYZList is updated; +:Changes made are saved to local data; +:Edit Success Message is returned; +stop + +@enduml diff --git a/docs/diagrams/FindCommandActivityDiagram.puml b/docs/diagrams/FindCommandActivityDiagram.puml new file mode 100644 index 00000000000..197ee930ccb --- /dev/null +++ b/docs/diagrams/FindCommandActivityDiagram.puml @@ -0,0 +1,30 @@ +@startuml +!include style.puml + +start +:User executes FindXYZCommand; +:FindXYZCommandParser extracts the relevant information +from the input argument; + +'Since the beta syntax does not support placing the condition outside the +'diamond we place it as the true branch instead. + +if () then ([Invalid Input Argument]) + :Display error message + on invalid command argument; + stop +else ([else]) + :Create XYZContainsKeywordPredicate; + repeat; + :Check if current XYZ + matches predicate; + if () then ([true]) + : Keep XYZ in filtered list; + else ([false]) + + endif + repeat while () is ([Have remaining XYZ in XYZList]) not ([else]) +: Show all filtered XYZ in FilteredXYZList; +: Success message is returned; +stop +@enduml diff --git a/docs/diagrams/ItemClassDiagram.puml b/docs/diagrams/ItemClassDiagram.puml new file mode 100644 index 00000000000..93d9bd791f2 --- /dev/null +++ b/docs/diagrams/ItemClassDiagram.puml @@ -0,0 +1,23 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor MODEL_COLOR +skinparam classBackgroundColor MODEL_COLOR + + +Enum "<>\nModelEnum" as ModelEnum{ +SUPPLIER +TASK +ORDER +CUSTOMER +} +Abstract Class "{abstract}\nItem" as Item +Class ItemList +Class "UniqueItemList " as UniqueItemList + + +Item -right-> "1 " ModelEnum +UniqueItemList -up-> " *" Item +ItemList -left-> " 1" UniqueItemList + +@enduml diff --git a/docs/diagrams/ListCommandActivityDiagram.puml b/docs/diagrams/ListCommandActivityDiagram.puml new file mode 100644 index 00000000000..9eca606c909 --- /dev/null +++ b/docs/diagrams/ListCommandActivityDiagram.puml @@ -0,0 +1,11 @@ +@startuml +!include style.puml + + start + :User executes ListXYZCommand; + :Retrieve FilteredXYZList; + :FilteredXYZList is updated to have all XYZ objects; + :XYZList is shown to the users; + :Success message is returned; + stop + @enduml diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index d4193173e18..3502e9fcd10 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -6,7 +6,7 @@ skinparam classBackgroundColor LOGIC_COLOR package Logic { -Class AddressBookParser +Class TrackrParser Class XYZCommand Class CommandResult Class "{abstract}\nCommand" as Command @@ -27,8 +27,8 @@ Class HiddenOutside #FFFFFF HiddenOutside ..> Logic LogicManager .right.|> Logic -LogicManager -right->"1" AddressBookParser -AddressBookParser ..> XYZCommand : creates > +LogicManager -right->"1" TrackrParser +TrackrParser ..> XYZCommand : creates > XYZCommand -up-|> Command LogicManager .left.> Command : executes > @@ -38,7 +38,7 @@ LogicManager --> Storage Storage --[hidden] Model Command .[hidden]up.> Storage Command .right.> Model -note right of XYZCommand: XYZCommand = AddCommand, \nFindCommand, etc +note right of XYZCommand: XYZCommand = AddOrderCommand, \nFindOrderCommand, etc Logic ..> CommandResult LogicManager .down.> CommandResult diff --git a/docs/diagrams/MenuClassDiagram.puml b/docs/diagrams/MenuClassDiagram.puml new file mode 100644 index 00000000000..955c7bd4144 --- /dev/null +++ b/docs/diagrams/MenuClassDiagram.puml @@ -0,0 +1,49 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor MODEL_COLOR +skinparam classBackgroundColor MODEL_COLOR +skinparam groupInheritance 1 + +top to bottom direction + +Abstract Class "{abstract}\nItem" as Item +Abstract Class "{abstract}\nName" as Name +Class "UniqueItemList " as UniqueItemList +Class ItemList + + +Class MenuItem +Class ItemSellingPrice +Class ItemProfit +Class ItemPrice +Class ItemCost +Class ItemName +Class Menu extends ItemList +Class UniqueMenuItemList extends UniqueItemList +Class ItemSellingPrice extends ItemPrice +Class ItemCost extends ItemPrice + + +ItemList *--r> "1" UniqueItemList +UniqueItemList -r>"*" Item + +Menu *--r> "1 " UniqueMenuItemList +UniqueMenuItemList -r>"*" MenuItem + +MenuItem *--> " 1" ItemSellingPrice +MenuItem *--> "1 " ItemProfit +MenuItem *--> " 1" ItemCost +MenuItem *--> "1" ItemName + +ItemProfit .left.> " 1 " ItemSellingPrice +ItemProfit ..> " 1 " ItemCost + + + + +MenuItem -u|> Item + +ItemName --|> Name + +@enduml diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 4439108973a..b5f28f874be 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -4,47 +4,42 @@ skinparam arrowThickness 1.1 skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR -Package Model <>{ -Class "<>\nReadOnlyAddressBook" as ReadOnlyAddressBook +top to bottom direction + +package "Model " MODEL_COLOR_T1 { +Class "<>\nReadOnlyXYZList" as ReadOnlyXYZList Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs Class "<>\nModel" as Model -Class AddressBook +Class XYZList Class ModelManager Class UserPrefs - -Class UniquePersonList -Class Person -Class Address -Class Email -Class Name -Class Phone -Class Tag - +Class UniqueXYZList +Class XYZ +Class "{abstract}\nItem" as Item +Class ItemList +Class "UniqueItemList " as UniqueItemList } Class HiddenOutside #FFFFFF HiddenOutside ..> Model -AddressBook .up.|> ReadOnlyAddressBook +ModelManager ..u|> Model +Model ..r> ReadOnlyUserPrefs +Model ..l> ReadOnlyXYZList +ModelManager --l> " 1" XYZList +ModelManager --r> "1 " UserPrefs +UserPrefs ..u> ReadOnlyUserPrefs -ModelManager .up.|> Model -Model .right.> ReadOnlyUserPrefs -Model .left.> ReadOnlyAddressBook -ModelManager -left-> "1" AddressBook -ModelManager -right-> "1" UserPrefs -UserPrefs .up.|> ReadOnlyUserPrefs +XYZList ..u|> ReadOnlyXYZList +XYZList *--d> " 1" UniqueXYZList +UniqueXYZList --> "*" XYZ : contains all > -AddressBook *--> "1" UniquePersonList -UniquePersonList --> "~* all" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag +ModelManager ----> "*" XYZ : contains filtered > -Name -[hidden]right-> Phone -Phone -[hidden]right-> Address -Address -[hidden]right-> Email +ItemList *-d> "1 " UniqueItemList +UniqueItemList -d> " *" Item -ModelManager -->"~* filtered" Person +UniqueXYZList --l|> UniqueItemList +XYZList --l|> ItemList +XYZ --l|> Item @enduml diff --git a/docs/diagrams/OrderClassDiagram.puml b/docs/diagrams/OrderClassDiagram.puml new file mode 100644 index 00000000000..c8e8a1398fe --- /dev/null +++ b/docs/diagrams/OrderClassDiagram.puml @@ -0,0 +1,44 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor MODEL_COLOR +skinparam classBackgroundColor MODEL_COLOR +skinparam groupInheritance 1 + +top to bottom direction + +Abstract Class "{abstract}\nItem" as Item +Abstract Class "{abstract}\nDeadline" as Deadline +Abstract Class "{abstract}\nStatus" as Status +Class "UniqueItemList " as UniqueItemList +Class ItemList + +Class LocalDateTime +Class Order +Class MenuItem +Class Customer +Class OrderQuantity +Class OrderStatus +Class OrderDeadline +Class OrderList extends ItemList +Class UniqueOrderList extends UniqueItemList +Class ItemList + + +ItemList *--r> "1" UniqueItemList +UniqueItemList -r>"*" Item + +OrderList *--r> "1 " UniqueOrderList +UniqueOrderList ->"*" Order +Order *--> "1" MenuItem +Order *--> "1" OrderDeadline +Order *--> "1 " OrderStatus +Order *--> "1 " Customer +Order *--> "1 " OrderQuantity +Order *--> "1 " LocalDateTime +Order -u|> Item + +OrderDeadline --|> Deadline +OrderStatus --|> Status + +@enduml diff --git a/docs/diagrams/PersonClassDiagram.puml b/docs/diagrams/PersonClassDiagram.puml new file mode 100644 index 00000000000..430052a46f2 --- /dev/null +++ b/docs/diagrams/PersonClassDiagram.puml @@ -0,0 +1,49 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor MODEL_COLOR +skinparam classBackgroundColor MODEL_COLOR +skinparam groupInheritance 1 + +Abstract Class "{abstract}\nItem" as Item +Abstract Class "{abstract}\nPerson" as Person +Class Supplier +Class Customer +Abstract Class "{abstract}\nName" as Name + +together { +Class PersonName +Class PersonAddress +Class PersonEmail +Class PersonPhone +Class Tag +} + +Class ItemList +Class "UniqueItemList " as UniqueItemList + +ItemList *--> "1" UniqueItemList +UniqueItemList --> "*" Item + +SupplierList *--> "1" UniqueSupplierList +UniqueSupplierList --> "*" Supplier +SupplierList --l|> ItemList +UniqueSupplierList --l|> UniqueItemList +CustomerList *--d> "1" UniqueCustomerList +UniqueCustomerList --d> " *" Customer +CustomerList --r|> ItemList +UniqueCustomerList --r|> UniqueItemList + +Person *--> "1" PersonName +Person *--> "1" PersonPhone +Person *--> "1" PersonEmail +Person *--> "1" PersonAddress +Person *--> "*" Tag +Person --u|> Item + +Supplier --|> Person +Customer --|> Person + +PersonName --|> Name + +@enduml diff --git a/docs/diagrams/SortCommandActivityDiagram.puml b/docs/diagrams/SortCommandActivityDiagram.puml new file mode 100644 index 00000000000..2b885843b0e --- /dev/null +++ b/docs/diagrams/SortCommandActivityDiagram.puml @@ -0,0 +1,19 @@ +@startuml +skinparam activityFontSize 18 +skinparam activityArrowFontSize 18 + +start +:User enters command to sort XYZ List; +:SortXYZCommandParser + parses the user input; +if () then ([Invalid command format]) + :Display Invalid Command Format + error message; + stop +else ([else]) +:Create SortXYZComparator; +:Sort FilteredXYZList using SortXYZComparator; +:Success message is returned; +stop + +@enduml diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 760305e0e58..1ec1baf4ca1 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -14,12 +14,14 @@ Class JsonUserPrefsStorage Class "<>\nStorage" as Storage Class StorageManager -package "AddressBook Storage" #F4F6F6{ -Class "<>\nAddressBookStorage" as AddressBookStorage -Class JsonAddressBookStorage -Class JsonSerializableAddressBook -Class JsonAdaptedPerson +package "Trackr Storage" #F4F6F6{ +Class "<>\nTrackrStorage" as TrackrStorage +Class JsonTrackrStorage +Class JsonSerializableTrackr +Class JsonAdaptedSupplier Class JsonAdaptedTag +Class JsonAdaptedTask +Class JsonAdaptedOrder } } @@ -29,15 +31,17 @@ HiddenOutside ..> Storage StorageManager .up.|> Storage StorageManager -up-> "1" UserPrefsStorage -StorageManager -up-> "1" AddressBookStorage +StorageManager -up-> "1" TrackrStorage Storage -left-|> UserPrefsStorage -Storage -right-|> AddressBookStorage +Storage -right-|> TrackrStorage JsonUserPrefsStorage .up.|> UserPrefsStorage -JsonAddressBookStorage .up.|> AddressBookStorage -JsonAddressBookStorage ..> JsonSerializableAddressBook -JsonSerializableAddressBook --> "*" JsonAdaptedPerson -JsonAdaptedPerson --> "*" JsonAdaptedTag +JsonTrackrStorage .up.|> TrackrStorage +JsonTrackrStorage ..> JsonSerializableTrackr +JsonSerializableTrackr --> "*" JsonAdaptedSupplier +JsonAdaptedSupplier --> "*" JsonAdaptedTag +JsonSerializableTrackr --> "*" JsonAdaptedOrder +JsonSerializableTrackr --> "*" JsonAdaptedTask @enduml diff --git a/docs/diagrams/TabCommandActivityDiagram.puml b/docs/diagrams/TabCommandActivityDiagram.puml new file mode 100644 index 00000000000..c36055abdc1 --- /dev/null +++ b/docs/diagrams/TabCommandActivityDiagram.puml @@ -0,0 +1,27 @@ +@startuml +!include style.puml + +start +:User executes TabCommand; +:TabCommandParser extracts the relevant zero-based index +from the supplied Target Tab name from TabEnum; + +'Since the beta syntax does not support placing the condition outside the +'diamond we place it as the true branch instead. + +if () then ([Invalid Index Argument]) + :Display error message + on invalid command argument; + stop +else ([else]) + :Get zero-based index; + :Update ObservableTabIndex to the index; + :Listener in Ui observes the change; + if () then ([No change in + ObservableTabIndex]) + :Same Tab is shown; + stop + else ([else]) + :Target Tab is shown; + stop +@enduml diff --git a/docs/diagrams/TaskClassDiagram.puml b/docs/diagrams/TaskClassDiagram.puml new file mode 100644 index 00000000000..af682d6472b --- /dev/null +++ b/docs/diagrams/TaskClassDiagram.puml @@ -0,0 +1,40 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor MODEL_COLOR +skinparam classBackgroundColor MODEL_COLOR +skinparam groupInheritance 1 + +top to bottom direction + +Abstract Class "{abstract}\nItem" as Item +Abstract Class "{abstract}\nName" as Name +Abstract Class "{abstract}\nDeadline" as Deadline +Abstract Class "{abstract}\nStatus" as Status +Class "UniqueItemList " as UniqueItemList +Class ItemList + +Class LocalDateTime +Class Task +Class TaskName +Class TaskStatus +Class TaskDeadline +Class TaskList extends ItemList +Class UniqueTaskList extends UniqueItemList + +ItemList *--r> "1" UniqueItemList +UniqueItemList -r> "*" Item + +TaskList *--r> "1 " UniqueTaskList +UniqueTaskList -r> "*" Task +Task *--> "1" TaskName +Task *--> " 1" TaskDeadline +Task *--> "1 " TaskStatus +Task *--> "1" LocalDateTime +Task -u|> Item + +TaskName --|> Name +TaskDeadline --|> Deadline +TaskStatus --|> Status + +@enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 95473d5aa19..171f1ddf1b6 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -4,25 +4,28 @@ skinparam arrowThickness 1.1 skinparam arrowColor UI_COLOR_T4 skinparam classBackgroundColor UI_COLOR -package UI <>{ +package UI <> UI_COLOR_T1 { Class "<>\nUi" as Ui Class "{abstract}\nUiPart" as UiPart Class UiManager Class MainWindow Class HelpWindow Class ResultDisplay -Class PersonListPanel -Class PersonCard +Class TabPanel +Class XYZListPanel +Class XYZCard +Class HomeView +Class FinanceCard Class StatusBarFooter Class CommandBox } -package Model <> { -Class HiddenModel #FFFFFF +package Model <> MODEL_COLOR_T1 { +Class " " as HiddenModel MODEL_COLOR_T1 } -package Logic <> { -Class HiddenLogic #FFFFFF +package Logic <> LOGIC_COLOR_T1 { +Class " " as HiddenLogic LOGIC_COLOR_T1 } Class HiddenOutside #FFFFFF @@ -32,29 +35,41 @@ UiManager .left.|> Ui UiManager -down-> "1" MainWindow MainWindow *-down-> "1" CommandBox MainWindow *-down-> "1" ResultDisplay -MainWindow *-down-> "1" PersonListPanel +MainWindow *-down-> "1" TabPanel MainWindow *-down-> "1" StatusBarFooter MainWindow --> "0..1" HelpWindow -PersonListPanel -down-> "*" PersonCard +TabPanel -down-> "1" HomeView +TabPanel -down-> "*" XYZListPanel -MainWindow -left-|> UiPart +XYZListPanel -down-> "*" XYZCard + +HomeView -down-> "*" XYZListPanel +HomeView -down-> "1" FinanceCard ResultDisplay --|> UiPart CommandBox --|> UiPart -PersonListPanel --|> UiPart -PersonCard --|> UiPart StatusBarFooter --|> UiPart HelpWindow --|> UiPart +XYZCard --|> UiPart +FinanceCard --|> UiPart +MainWindow -left-|> UiPart + +XYZCard ..> Model +FinanceCard ..> Model -PersonCard ..> Model UiManager -right-> Logic MainWindow -left-> Logic -PersonListPanel -[hidden]left- HelpWindow -HelpWindow -[hidden]left- CommandBox +HomeView -[hidden]right- XYZListPanel + +note right of XYZListPanel: XYZListPanel = TaskListView, \n OrderListView, MenuItemListView etc. +note right of XYZCard: XYZCard = TaskCard, OrderCard, \n MenuItemCard etc. + +TabPanel -[hidden]left- CommandBox CommandBox -[hidden]left- ResultDisplay ResultDisplay -[hidden]left- StatusBarFooter +StatusBarFooter -[hidden]left- HelpWindow MainWindow -[hidden]-|> UiPart @enduml diff --git a/docs/diagrams/UndoRedoState0.puml b/docs/diagrams/UndoRedoState0.puml index 96e30744d24..e6094627e3c 100644 --- a/docs/diagrams/UndoRedoState0.puml +++ b/docs/diagrams/UndoRedoState0.puml @@ -6,15 +6,15 @@ 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 "__t0:Trackr__" STATE_COLOR + class State2 as "__t1:Trackr__" STATE_COLOR + class State3 as "__t2:Trackr__" STATE_COLOR } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 hide State2 hide State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" Pointer -up-> State1 @end diff --git a/docs/diagrams/UndoRedoState1.puml b/docs/diagrams/UndoRedoState1.puml index 01fcb9b2b96..4da3279210f 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 "delete_task 5" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__t0:Trackr__" STATE_COLOR + class State2 as "__t1:Trackr__" STATE_COLOR + class State3 as "__t2:Trackr__" STATE_COLOR } State1 -[hidden]right-> State2 @@ -16,7 +16,7 @@ State2 -[hidden]right-> State3 hide State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" Pointer -up-> State2 @end diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml index bccc230a5d1..d38d23cb653 100644 --- a/docs/diagrams/UndoRedoState2.puml +++ b/docs/diagrams/UndoRedoState2.puml @@ -3,18 +3,18 @@ skinparam ClassFontColor #000000 skinparam ClassBorderColor #000000 -title After command "add n/David" +title After command "add_task n/Sort Storage d/01/01/2024" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__t0:Trackr__" STATE_COLOR + class State2 as "__t1:Trackr__" STATE_COLOR + class State3 as "__t2:Trackr__" STATE_COLOR } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" Pointer -up-> State3 @end diff --git a/docs/diagrams/UndoRedoState3.puml b/docs/diagrams/UndoRedoState3.puml index ea29c9483e4..9e98d33ed2a 100644 --- a/docs/diagrams/UndoRedoState3.puml +++ b/docs/diagrams/UndoRedoState3.puml @@ -6,15 +6,15 @@ 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 "__t0:Trackr__" STATE_COLOR + class State2 as "__t1:Trackr__" STATE_COLOR + class State3 as "__t2:Trackr__" STATE_COLOR } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" Pointer -up-> State2 @end diff --git a/docs/diagrams/UndoRedoState4.puml b/docs/diagrams/UndoRedoState4.puml index 1b784cece80..f772010147a 100644 --- a/docs/diagrams/UndoRedoState4.puml +++ b/docs/diagrams/UndoRedoState4.puml @@ -6,15 +6,15 @@ skinparam ClassBorderColor #000000 title After command "list" package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" + class State1 as "__t0:Trackr__" STATE_COLOR + class State2 as "__t1:Trackr__" STATE_COLOR + class State3 as "__t2:Trackr__" STATE_COLOR } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" Pointer -up-> State2 @end diff --git a/docs/diagrams/UndoRedoState5.puml b/docs/diagrams/UndoRedoState5.puml index 88927be32bc..2cf2749ada9 100644 --- a/docs/diagrams/UndoRedoState5.puml +++ b/docs/diagrams/UndoRedoState5.puml @@ -6,16 +6,16 @@ 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 "__t0:Trackr__" STATE_COLOR + class State2 as "__t1:Trackr__" STATE_COLOR + class State3 as "__t2:Trackr__" STATE_COLOR } State1 -[hidden]right-> State2 State2 -[hidden]right-> State3 -class Pointer as "Current State" #FFFFF +class Pointer as "Current State" Pointer -up-> State3 -note right on link: State ab2 deleted. +note right on link #YELLOW : State ab2 deleted. @end diff --git a/docs/diagrams/UndoSequenceDiagram.puml b/docs/diagrams/UndoSequenceDiagram.puml index 410aab4e412..e38e19935ab 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 ":TrackrParser" as TrackrParser LOGIC_COLOR participant "u: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 ":VersionedTrackr" as VersionedTrackr MODEL_COLOR end box [-> LogicManager : execute(undo) activate LogicManager -LogicManager -> AddressBookParser : parseCommand(undo) -activate AddressBookParser +LogicManager -> TrackrParser : parseCommand(undo) +activate TrackrParser create UndoCommand -AddressBookParser -> UndoCommand +TrackrParser -> UndoCommand activate UndoCommand -UndoCommand --> AddressBookParser +UndoCommand --> TrackrParser deactivate UndoCommand -AddressBookParser --> LogicManager : u -deactivate AddressBookParser +TrackrParser --> LogicManager : u +deactivate TrackrParser LogicManager -> UndoCommand : execute() activate UndoCommand -UndoCommand -> Model : undoAddressBook() +UndoCommand -> Model : undoTrackr() activate Model -Model -> VersionedAddressBook : undo() -activate VersionedAddressBook +Model -> VersionedTrackr : undo() +activate VersionedTrackr -VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook) -VersionedAddressBook --> Model : -deactivate VersionedAddressBook +VersionedTrackr -> VersionedTrackr :resetData(ReadOnlyTrackr) +VersionedTrackr --> Model : +deactivate VersionedTrackr Model --> UndoCommand deactivate Model diff --git a/docs/diagrams/style.puml b/docs/diagrams/style.puml index fad8b0adeaa..04bdcecebe5 100644 --- a/docs/diagrams/style.puml +++ b/docs/diagrams/style.puml @@ -33,15 +33,19 @@ !define USER_COLOR #000000 +!define STATE_COLOR #FEFECE + +' Set White Background skinparam BackgroundColor #FFFFFFF +' Remove Shadows skinparam Shadowing false skinparam Class { FontColor #FFFFFF - BorderThickness 1 - BorderColor #FFFFFF - StereotypeFontColor #FFFFFF + BorderThickness 0 +' BorderColor #FFFFFF +' StereotypeFontColor #FFFFFF FontName Arial } diff --git a/docs/images/AddCommandActivityDiagram.svg b/docs/images/AddCommandActivityDiagram.svg new file mode 100644 index 00000000000..2a54482385f --- /dev/null +++ b/docs/images/AddCommandActivityDiagram.svg @@ -0,0 +1,188 @@ + + + + + + + User executes AddXYZCommand + + AddXYZCommandParser extracts the relevant information + from the input argument + + [Invalid Input Argument] + [else] + + Display error message + on invalid command argument + + + + Create XYZ object + + [List has Item] + [else] + + Display error message + on duplicate item + + + + Add XYZ to XYZ XYZList + + Update XYZ in FilteredXYZList + + Save XYZ to local data with internal XYZList + + Success message is returned + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/ArchitectureDiagram.png b/docs/images/ArchitectureDiagram.png deleted file mode 100644 index 86c60246ccb..00000000000 Binary files a/docs/images/ArchitectureDiagram.png and /dev/null differ diff --git a/docs/images/ArchitectureDiagram.svg b/docs/images/ArchitectureDiagram.svg new file mode 100644 index 00000000000..d35bb2c73d3 --- /dev/null +++ b/docs/images/ArchitectureDiagram.svg @@ -0,0 +1,148 @@ + + + + + + +   + + + + + UI + + + + + Logic + + + + + Storage + + + + + Model + + + + + Main + + + + + Commons + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png deleted file mode 100644 index 2f1346869d0..00000000000 Binary files a/docs/images/ArchitectureSequenceDiagram.png and /dev/null differ diff --git a/docs/images/ArchitectureSequenceDiagram.svg b/docs/images/ArchitectureSequenceDiagram.svg new file mode 100644 index 00000000000..5ea38b5dc0c --- /dev/null +++ b/docs/images/ArchitectureSequenceDiagram.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + User + + + + + :UI + + + :Logic + + + :Model + + + :Storage + + + + + + + + + "delete_supplier 1" + + + + execute("delete_supplier 1") + + + + deleteItem(p, ModelEnum.SUPPLIER) + + + + + + saveTrackr(supplierList, taskList, orderList) + + + + + + Save to file + + + + + + + + + + + + + diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png deleted file mode 100644 index 94440f0ac4a..00000000000 Binary files a/docs/images/BetterModelClassDiagram.png and /dev/null differ diff --git a/docs/images/ClearCommandActivityDiagram.svg b/docs/images/ClearCommandActivityDiagram.svg new file mode 100644 index 00000000000..11e92c922a1 --- /dev/null +++ b/docs/images/ClearCommandActivityDiagram.svg @@ -0,0 +1,45 @@ + + + + + + + User enters ClearXYZCommand + + + Trackr removes all XYZ items from XYZList + + + Save data into data file + + + Shows success message + + + + + + + + + + + + + + + diff --git a/docs/images/CommitActivityDiagram.png b/docs/images/CommitActivityDiagram.png deleted file mode 100644 index c08c13f5c8b..00000000000 Binary files a/docs/images/CommitActivityDiagram.png and /dev/null differ diff --git a/docs/images/CommitActivityDiagram.svg b/docs/images/CommitActivityDiagram.svg new file mode 100644 index 00000000000..60492c11986 --- /dev/null +++ b/docs/images/CommitActivityDiagram.svg @@ -0,0 +1,39 @@ +User executes commandPurge redundant statesSave Trackr totrackrStateList[command commits AddressBook][else] diff --git a/docs/images/ComponentManagers.png b/docs/images/ComponentManagers.png deleted file mode 100644 index b5764ff9273..00000000000 Binary files a/docs/images/ComponentManagers.png and /dev/null differ diff --git a/docs/images/ComponentManagers.svg b/docs/images/ComponentManagers.svg new file mode 100644 index 00000000000..5678a024c9c --- /dev/null +++ b/docs/images/ComponentManagers.svg @@ -0,0 +1,125 @@ + + + + + + + Logic + + + + + Model + + + + + Storage + + + + + «interface» + + Logic + + + + + LogicManager + + + + + «interface» + + Model + + + + + ModelManager + + + + + «interface» + + Storage + + + + + StorageManager + + + + + HiddenOutside + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/CorruptedData.png b/docs/images/CorruptedData.png new file mode 100644 index 00000000000..fb2d2ba6cc3 Binary files /dev/null and b/docs/images/CorruptedData.png differ diff --git a/docs/images/CorruptedOrderList.png b/docs/images/CorruptedOrderList.png new file mode 100644 index 00000000000..9d56288c7e4 Binary files /dev/null and b/docs/images/CorruptedOrderList.png differ diff --git a/docs/images/DeleteCommandActivityDiagram.svg b/docs/images/DeleteCommandActivityDiagram.svg new file mode 100644 index 00000000000..f142410b9c9 --- /dev/null +++ b/docs/images/DeleteCommandActivityDiagram.svg @@ -0,0 +1 @@ +User executes DeleteXYZCommandDeleteXYZCommandParser extracts the relevant one-based indexfrom the input argument[Invalid Index Argument][else]Display error messageon invalid command argumentGet zero-based index from one-based indexCheck out-of-bounds condition for index on FilteredXYZList[true][false]Display invalid XYZ index error messageRetrieve item to delete from FilteredXYZListItem is deleted from XYZListXYZList with XYZ removed is saved to local storageSuccess message is returned diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png deleted file mode 100644 index fa327b39618..00000000000 Binary files a/docs/images/DeleteSequenceDiagram.png and /dev/null differ diff --git a/docs/images/DeleteSequenceDiagram.svg b/docs/images/DeleteSequenceDiagram.svg new file mode 100644 index 00000000000..b57e61622e7 --- /dev/null +++ b/docs/images/DeleteSequenceDiagram.svg @@ -0,0 +1,182 @@ + + + + + + Logic + + + Model + + + + + + + + + + + + + + + + + :LogicManager + + + :TrackrParser + + + :Model + + + + + + + + + + + + execute("delete_order 1") + + + + parseCommand("delete_order 1") + + + + + :DeleteOrder + + CommandParser + + + + + + parse("1") + + + + + d:DeleteOrder + + Command + + + + d + + + + d + + + + + + d + + + + execute() + + + + deleteItem(1, + + ModelEnum.ORDER) + + + + + + + :CommandResult + + + + + + result + + + + + diff --git a/docs/images/EditCommandActivityDiagram.svg b/docs/images/EditCommandActivityDiagram.svg new file mode 100644 index 00000000000..635a9902eeb --- /dev/null +++ b/docs/images/EditCommandActivityDiagram.svg @@ -0,0 +1,82 @@ +User enters commandto edit XYZ in XYZ ListEditXYZCommandParserparses the user input[Invalid command format][else]Display Invalid Command Formaterror messageChange one-based indexto zero-based indexCheck if index given is valid[Index is out of bounds][else]Display Invalid ItemIndex error messageEdit item XYZ diff --git a/docs/images/EditItemXYZRakeActivityDiagram.svg b/docs/images/EditItemXYZRakeActivityDiagram.svg new file mode 100644 index 00000000000..d5eb5cfe702 --- /dev/null +++ b/docs/images/EditItemXYZRakeActivityDiagram.svg @@ -0,0 +1,38 @@ +Activity: Edit item XYZ Item XYZ at index (zero-based)retrieved from its FilteredXYZListCopy of the item XYZ is made and edited [Edited item is considered to be the same asother items in the list or same as original item] [else] Display duplicate item error messageItem in XYZ List is editedFilteredXYZList is updatedChanges made are saved to local dataEdit Success Message is returned diff --git a/docs/images/FindCommandActivityDiagram.svg b/docs/images/FindCommandActivityDiagram.svg new file mode 100644 index 00000000000..c682a08be07 --- /dev/null +++ b/docs/images/FindCommandActivityDiagram.svg @@ -0,0 +1,154 @@ + + + + + + + User executes FindXYZCommand + + + FindXYZCommandParser extracts the relevant information + + from the input argument + + + [Invalid Input Argument] + + [else] + + + Display error message + + on invalid command argument + + + + + Create XYZContainsKeywordPredicate + + + Check if current XYZ + + matches predicate + + + Keep XYZ in filtered list + + + [true] + + [false] + + + + + [else] + + [Have remaining XYZ in XYZList] + + + Show all filtered XYZ in FilteredXYZList + + + Success message is returned + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/ItemClassDiagram.svg b/docs/images/ItemClassDiagram.svg new file mode 100644 index 00000000000..591a5b396ba --- /dev/null +++ b/docs/images/ItemClassDiagram.svg @@ -0,0 +1,74 @@ + + + + + + + «enumeration» + + ModelEnum + + + + + {abstract} + + Item + + + + + ItemList + + + T extends Item + + + + + UniqueItemList<T> + + + + + + 1 + + + + + + * + + + + + + 1 + + + + diff --git a/docs/images/ListCommandActivityDiagram.svg b/docs/images/ListCommandActivityDiagram.svg new file mode 100644 index 00000000000..d139d085192 --- /dev/null +++ b/docs/images/ListCommandActivityDiagram.svg @@ -0,0 +1,79 @@ +User executes ListXYZCommandRetrieve FilteredXYZListFilteredXYZList is updated to have all XYZ objectsXYZList is shown to the usersSuccess message is returned diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png deleted file mode 100644 index 9e9ba9f79e5..00000000000 Binary files a/docs/images/LogicClassDiagram.png and /dev/null differ diff --git a/docs/images/LogicClassDiagram.svg b/docs/images/LogicClassDiagram.svg new file mode 100644 index 00000000000..d183a3a48ec --- /dev/null +++ b/docs/images/LogicClassDiagram.svg @@ -0,0 +1,173 @@ +LogicModelTrackrParserXYZCommandCommandResult{abstract}Command<<interface>>LogicLogicManagerHiddenModelStorageHiddenOutsideXYZCommand = AddOrderCommand,FindOrderCommand, etc1createsexecutesproduces diff --git a/docs/images/MenuClassDiagram.svg b/docs/images/MenuClassDiagram.svg new file mode 100644 index 00000000000..e2ae8c5cff6 --- /dev/null +++ b/docs/images/MenuClassDiagram.svg @@ -0,0 +1,326 @@ + + + + + + + {abstract} + Item + + + + {abstract} + Name + + + + UniqueItemList<T> + + + + ItemList + + T extends Item + + + + MenuItem + + + + ItemSellingPrice + + + + ItemProfit + + + + ItemPrice + + + + ItemCost + + + + ItemName + + + + Menu + + + + UniqueMenuItemList + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + * + + + + + + + 1 + + + + + * + + + + + + + 1 + + + + + + + 1 + + + + + + + 1 + + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png deleted file mode 100644 index 04070af60d8..00000000000 Binary files a/docs/images/ModelClassDiagram.png and /dev/null differ diff --git a/docs/images/ModelClassDiagram.svg b/docs/images/ModelClassDiagram.svg new file mode 100644 index 00000000000..3a760a26773 --- /dev/null +++ b/docs/images/ModelClassDiagram.svg @@ -0,0 +1,258 @@ + + + + + + + Model + + + + + «interface» + + ReadOnlyXYZList + + + + + «interface» + + ReadOnlyUserPrefs + + + + + «interface» + + Model + + + + + XYZList + + + + + ModelManager + + + + + UserPrefs + + + + + UniqueXYZList + + + + + XYZ + + + + + {abstract} + + Item + + + + + ItemList + + + T extends Item + + + + + UniqueItemList<T> + + + + + HiddenOutside + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + 1 + + + + + + + + + + + + + + + + 1 + + + + + + + contains all + + * + + + + + + + contains filtered + + * + + + + + + + + 1 + + + + + + * + + + + + + + + + + + + + + + + diff --git a/docs/images/OrderClassDiagram.svg b/docs/images/OrderClassDiagram.svg new file mode 100644 index 00000000000..3061f49b5a6 --- /dev/null +++ b/docs/images/OrderClassDiagram.svg @@ -0,0 +1,326 @@ + + + + + + + {abstract} + Item + + + + {abstract} + Deadline + + + + {abstract} + Status + + + + UniqueItemList<T> + + + + ItemList + + T extends Item + + + + LocalDateTime + + + + Order + + + + MenuItem + + + + Customer + + + + OrderQuantity + + + + OrderStatus + + + + OrderDeadline + + + + OrderList + + + + UniqueOrderList + + + + + + + + + + + + + + + 1 + + + + + * + + + + + + + 1 + + + + + * + + + + + + + 1 + + + + + + + 1 + + + + + + + 1 + + + + + + + 1 + + + + + + + 1 + + + + + + + 1 + + + + + + + + + + + + + + diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png deleted file mode 100644 index e7b4c8880cd..00000000000 Binary files a/docs/images/ParserClasses.png and /dev/null differ diff --git a/docs/images/ParserClasses.svg b/docs/images/ParserClasses.svg new file mode 100644 index 00000000000..eb50e857eb2 --- /dev/null +++ b/docs/images/ParserClasses.svg @@ -0,0 +1,159 @@ +Parser classes<<interface>>ParserAddressBookParserXYZCommandParserCliSyntaxParserUtilArgumentMultimapArgumentTokenizerPrefix{abstract}CommandXYZCommandHiddenOutsidecreatescreatesreturns diff --git a/docs/images/PersonClassDiagram.svg b/docs/images/PersonClassDiagram.svg new file mode 100644 index 00000000000..a29f9776333 --- /dev/null +++ b/docs/images/PersonClassDiagram.svg @@ -0,0 +1,317 @@ + + + + + + + {abstract} + + Item + + + + + {abstract} + + Person + + + + + Supplier + + + + + Customer + + + + + {abstract} + + Name + + + + + PersonName + + + + + PersonAddress + + + + + PersonEmail + + + + + PersonPhone + + + + + Tag + + + + + ItemList + + + T extends Item + + + + + UniqueItemList<T> + + + + + SupplierList + + + + + UniqueSupplierList + + + + + CustomerList + + + + + UniqueCustomerList + + + + + + + + 1 + + + + + + * + + + + + + + + 1 + + + + + + * + + + + + + + + + + + + + + + + 1 + + + + + + * + + + + + + + + + + + + + + + + 1 + + + + + + + + 1 + + + + + + + + 1 + + + + + + + + 1 + + + + + + + + * + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/SortCommandActivityDiagram.svg b/docs/images/SortCommandActivityDiagram.svg new file mode 100644 index 00000000000..c59315f3c19 --- /dev/null +++ b/docs/images/SortCommandActivityDiagram.svg @@ -0,0 +1,29 @@ +User enters command to sort XYZ ListSortXYZCommandParserparses the user input[Invalid command format][else]Display Invalid Command Formaterror messageCreate SortXYZComparatorSort FilteredXYZList using SortXYZComparatorSuccess message is returned diff --git a/docs/images/SortOrdersExample.png b/docs/images/SortOrdersExample.png new file mode 100644 index 00000000000..da14f5ec14b Binary files /dev/null and b/docs/images/SortOrdersExample.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png deleted file mode 100644 index 2533a5c1af0..00000000000 Binary files a/docs/images/StorageClassDiagram.png and /dev/null differ diff --git a/docs/images/StorageClassDiagram.svg b/docs/images/StorageClassDiagram.svg new file mode 100644 index 00000000000..8041daec164 --- /dev/null +++ b/docs/images/StorageClassDiagram.svg @@ -0,0 +1,180 @@ +StorageUserPrefs StorageTrackr Storage<<interface>>StorageStorageManager<<interface>>UserPrefsStorageJsonUserPrefsStorage<<interface>>TrackrStorageJsonTrackrStorageJsonSerializableTrackrJsonAdaptedSupplierJsonAdaptedTagJsonAdaptedTaskJsonAdaptedOrderHiddenOutside11**** diff --git a/docs/images/TabCommandActivityDiagram.svg b/docs/images/TabCommandActivityDiagram.svg new file mode 100644 index 00000000000..068dc6b68df --- /dev/null +++ b/docs/images/TabCommandActivityDiagram.svg @@ -0,0 +1 @@ +User executes TabCommandTabCommandParser extracts the relevant zero-based indexfrom the supplied Target Tab name from TabEnum[Invalid Index Argument][else]Display error messageon invalid command argumentGet zero-based indexUpdate ObservableTabIndex to the indexListener in Ui observes the change[No change inObservableTabIndex][else]Same Tab is shownTarget Tab is shown diff --git a/docs/images/TaskClassDiagram.svg b/docs/images/TaskClassDiagram.svg new file mode 100644 index 00000000000..54bf596158d --- /dev/null +++ b/docs/images/TaskClassDiagram.svg @@ -0,0 +1,250 @@ + + + + + + + {abstract} + + Item + + + + + {abstract} + + Name + + + + + {abstract} + + Deadline + + + + + {abstract} + + Status + + + + + UniqueItemList<T> + + + + + ItemList + + + T extends Item + + + + + LocalDateTime + + + + + Task + + + + + TaskName + + + + + TaskStatus + + + + + TaskDeadline + + + + + TaskList + + + + + UniqueTaskList + + + + + + + + + + + + + + + + 1 + + + + + + * + + + + + + + + 1 + + + + + + * + + + + + + + + 1 + + + + + + + + 1 + + + + + + + + 1 + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5bd77847aa2..62da35198a1 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png deleted file mode 100644 index 785e04dbab4..00000000000 Binary files a/docs/images/UiClassDiagram.png and /dev/null differ diff --git a/docs/images/UiClassDiagram.svg b/docs/images/UiClassDiagram.svg new file mode 100644 index 00000000000..0c6da1c5718 --- /dev/null +++ b/docs/images/UiClassDiagram.svg @@ -0,0 +1 @@ +UIModelLogic«interface»Ui{abstract}UiPartUiManagerMainWindowHelpWindowResultDisplayTabPanelXYZListPanelXYZCardHomeViewFinanceCardStatusBarFooterCommandBox                                                                HiddenOutsideXYZListPanel = TaskListView,OrderListView, MenuItemListView etc.XYZCard = TaskCard, OrderCard,MenuItemCard etc.111110..11***1 diff --git a/docs/images/UndoRedoState0.png b/docs/images/UndoRedoState0.png deleted file mode 100644 index 8f7538cd884..00000000000 Binary files a/docs/images/UndoRedoState0.png and /dev/null differ diff --git a/docs/images/UndoRedoState0.svg b/docs/images/UndoRedoState0.svg new file mode 100644 index 00000000000..282e6f14a8b --- /dev/null +++ b/docs/images/UndoRedoState0.svg @@ -0,0 +1,107 @@ +Initial stateStatest0:TrackrCurrent State diff --git a/docs/images/UndoRedoState1.png b/docs/images/UndoRedoState1.png deleted file mode 100644 index df9908d0948..00000000000 Binary files a/docs/images/UndoRedoState1.png and /dev/null differ diff --git a/docs/images/UndoRedoState1.svg b/docs/images/UndoRedoState1.svg new file mode 100644 index 00000000000..55fe1eb178b --- /dev/null +++ b/docs/images/UndoRedoState1.svg @@ -0,0 +1,111 @@ +After command "delete_task 5"Statest0:Trackrt1:TrackrCurrent State diff --git a/docs/images/UndoRedoState2.png b/docs/images/UndoRedoState2.png deleted file mode 100644 index 36519c1015b..00000000000 Binary files a/docs/images/UndoRedoState2.png and /dev/null differ diff --git a/docs/images/UndoRedoState2.svg b/docs/images/UndoRedoState2.svg new file mode 100644 index 00000000000..c9947ee9faa --- /dev/null +++ b/docs/images/UndoRedoState2.svg @@ -0,0 +1,107 @@ +After command "add_task n/Sort Storage d/01/01/2024"Statest0:Trackrt1:Trackrt2:TrackrCurrent State diff --git a/docs/images/UndoRedoState3.png b/docs/images/UndoRedoState3.png deleted file mode 100644 index 19959d01712..00000000000 Binary files a/docs/images/UndoRedoState3.png and /dev/null differ diff --git a/docs/images/UndoRedoState3.svg b/docs/images/UndoRedoState3.svg new file mode 100644 index 00000000000..ec448fddfcf --- /dev/null +++ b/docs/images/UndoRedoState3.svg @@ -0,0 +1,107 @@ +After command "undo"Statest0:Trackrt1:Trackrt2:TrackrCurrent State diff --git a/docs/images/UndoRedoState4.png b/docs/images/UndoRedoState4.png deleted file mode 100644 index 4c623e4f2c5..00000000000 Binary files a/docs/images/UndoRedoState4.png and /dev/null differ diff --git a/docs/images/UndoRedoState4.svg b/docs/images/UndoRedoState4.svg new file mode 100644 index 00000000000..0ab9e4b73bc --- /dev/null +++ b/docs/images/UndoRedoState4.svg @@ -0,0 +1,107 @@ +After command "list"Statest0:Trackrt1:Trackrt2:TrackrCurrent State diff --git a/docs/images/UndoRedoState5.png b/docs/images/UndoRedoState5.png deleted file mode 100644 index 84ad2afa6bd..00000000000 Binary files a/docs/images/UndoRedoState5.png and /dev/null differ diff --git a/docs/images/UndoRedoState5.svg b/docs/images/UndoRedoState5.svg new file mode 100644 index 00000000000..4861d33b158 --- /dev/null +++ b/docs/images/UndoRedoState5.svg @@ -0,0 +1,109 @@ +After command "clear"Statest0:Trackrt1:Trackrt2:TrackrCurrent StateState ab2 deleted. diff --git a/docs/images/UndoSequenceDiagram.png b/docs/images/UndoSequenceDiagram.png deleted file mode 100644 index 6addcd3a8d9..00000000000 Binary files a/docs/images/UndoSequenceDiagram.png and /dev/null differ diff --git a/docs/images/UndoSequenceDiagram.svg b/docs/images/UndoSequenceDiagram.svg new file mode 100644 index 00000000000..b0fe7b72b86 --- /dev/null +++ b/docs/images/UndoSequenceDiagram.svg @@ -0,0 +1,165 @@ +LogicModel:LogicManager:TrackrParser:Model:VersionedTrackrexecute(undo)parseCommand(undo)u:UndoCommanduexecute()undoTrackr()undo()resetData(ReadOnlyTrackr)result diff --git a/docs/images/arkarsg.png b/docs/images/arkarsg.png new file mode 100644 index 00000000000..d29ab5cdfd1 Binary files /dev/null and b/docs/images/arkarsg.png differ diff --git a/docs/images/changgittyhub.png b/docs/images/changgittyhub.png new file mode 100644 index 00000000000..632c9128d66 Binary files /dev/null and b/docs/images/changgittyhub.png differ diff --git a/docs/images/chongweiguan.png b/docs/images/chongweiguan.png new file mode 100644 index 00000000000..503712b220e Binary files /dev/null and b/docs/images/chongweiguan.png differ diff --git a/docs/images/hmuumyatmoe.png b/docs/images/hmuumyatmoe.png new file mode 100644 index 00000000000..e8235801a3a Binary files /dev/null and b/docs/images/hmuumyatmoe.png differ diff --git a/docs/images/liumc-sg.png b/docs/images/liumc-sg.png new file mode 100644 index 00000000000..899caaf07d1 Binary files /dev/null and b/docs/images/liumc-sg.png differ diff --git a/docs/images/ug/DataTabUILabeled.png b/docs/images/ug/DataTabUILabeled.png new file mode 100644 index 00000000000..ae75a86082a Binary files /dev/null and b/docs/images/ug/DataTabUILabeled.png differ diff --git a/docs/images/ug/HelpMessage.png b/docs/images/ug/HelpMessage.png new file mode 100644 index 00000000000..394759b53d7 Binary files /dev/null and b/docs/images/ug/HelpMessage.png differ diff --git a/docs/images/ug/HomePageUI.png b/docs/images/ug/HomePageUI.png new file mode 100644 index 00000000000..d1f230d770b Binary files /dev/null and b/docs/images/ug/HomePageUI.png differ diff --git a/docs/images/ug/HomePageUILabeled.png b/docs/images/ug/HomePageUILabeled.png new file mode 100644 index 00000000000..734e8354f4f Binary files /dev/null and b/docs/images/ug/HomePageUILabeled.png differ diff --git a/docs/images/ug/MenuCardLabeled.png b/docs/images/ug/MenuCardLabeled.png new file mode 100644 index 00000000000..d1815f0613b Binary files /dev/null and b/docs/images/ug/MenuCardLabeled.png differ diff --git a/docs/images/ug/OrderCardLabeled.png b/docs/images/ug/OrderCardLabeled.png new file mode 100644 index 00000000000..14c65d00bda Binary files /dev/null and b/docs/images/ug/OrderCardLabeled.png differ diff --git a/docs/images/ug/SupplierCardLabeled.png b/docs/images/ug/SupplierCardLabeled.png new file mode 100644 index 00000000000..bfb49f584a1 Binary files /dev/null and b/docs/images/ug/SupplierCardLabeled.png differ diff --git a/docs/images/ug/TaskCardLabeled.png b/docs/images/ug/TaskCardLabeled.png new file mode 100644 index 00000000000..c75077cddbd Binary files /dev/null and b/docs/images/ug/TaskCardLabeled.png differ diff --git a/docs/images/ug/ValidCsvFile.png b/docs/images/ug/ValidCsvFile.png new file mode 100644 index 00000000000..9528d9b8cb2 Binary files /dev/null and b/docs/images/ug/ValidCsvFile.png differ diff --git a/docs/img.png b/docs/img.png new file mode 100644 index 00000000000..445449ca210 Binary files /dev/null and b/docs/img.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..b6fb06d545c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,19 +1,20 @@ --- layout: page -title: AddressBook Level-3 +title: Trackr --- -[![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/ay2223s2-cs2103t-w15-2/tp/workflows/Java%20CI/badge.svg)](https://github.com/ay2223s2-cs2103t-w15-2/tp/actions) +[![codecov](https://codecov.io/gh/ay2223s2-cs2103t-w15-2/tp/branch/master/graph/badge.svg)](https://codecov.io/gh/ay2223s2-cs2103t-w15-2/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). +**Trackr is a desktop application for managing your supplier contact details, orders and tasks.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). -* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). -* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. +* If you are interested in using Trackr, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). +* If you are interested about developing Trackr, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. **Acknowledgements** * Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5) +* Based upon: [Address Book Product Website](https://se-education.org/addressbook-level3) diff --git a/docs/team/arkarsg.md b/docs/team/arkarsg.md new file mode 100644 index 00000000000..bbf83fcbadf --- /dev/null +++ b/docs/team/arkarsg.md @@ -0,0 +1,52 @@ +--- +layout: page +title: Arkar's Portfolio Page +--- + +### Project: Trackr + +Trackr is a desktop application used to keep track of order, menu, suppliers and tasks. 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. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=arkarsg&breakdown=true) + +* **Contribution to code base** + * Initial refactoring of `Supplier` from `Person` + * Justification: This improves the product because the differentiation between `Supplier` and `Person` fits the use case of our product. + * Refactor `Order` to have a reference to `MenuItem` + * Justification: This association is needed as users should only be able to create orders from pre-existing `MenuItem`. + * Highlight: + * Adapt `Storage` and `JSON` files to support `MenuItem` nested within `Order` + * Generate total profits and sales from each order and all orders + * Profits and sales responsive to changes in `OrderQuantity` and `OrderItem` + * Rewrite relevant test cases for Order + * Overhauls Ui + * Highlights: + * Refactor into packages + * Cosmetic changes using CSS + * Added tabbed views for different data + * New feature: `TabCommand` that allows users to switch between tabs + * Justification: Like most applications, users can use `CTRL + TAB` or `ARROW KEYS` to cycle through area of focus. This added feature allow users to skip tabs while using a verbose command. This also further optimises the product for CLI use. + * Highlight: Used the `Observer / Observable` pattern with JavaFX `SimpleIntegerProperty` to listen to changes in the selected tab index. This decouples `TabCommand` in `Logic` from `Ui` and enforces Law of Demeter. Some commands such as `list`/`find` also snaps to relevant tab for better user experience. + * Improve test coverage + * Spotted bugs and improved test coverage [\#271](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/271), [\#285](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/285), [\#297](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/297) + +* **Review / Peer Help** + * Helped teammates with debugging and test cases + +* **Documentation**: + * User Guide: + * Drafted command syntax and examples + * Provided overall feedback [\#267](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/267) + * Developer Guide: + * Updated UML diagram for `Ui` and its description + * Added activity diagram for `DeleteCommand` + * Updated user stories and use cases. + +* **Community**: + * PRs reviewed (with non-trivial review comments): [\#267](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/267), [\#177](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/177) + +* **Tools**: + * Java 11, JavaFx, Jekyll + diff --git a/docs/team/changgittyhub.md b/docs/team/changgittyhub.md new file mode 100644 index 00000000000..bd015b88853 --- /dev/null +++ b/docs/team/changgittyhub.md @@ -0,0 +1,60 @@ +--- +layout: page +title: Darren Chang's Project Portfolio Page +--- + +### Project: Trackr + +Trackr is a desktop tracking application used to keep track of order, menu, suppliers and tasks. 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. + +* Added `Menu` Model including all field classes [\#177](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/177) +* Updated Storage related classes to allow for the storage of `Menu` objects [\#177](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/177) +* Completed `add`, `delete`, `edit`, `clear`, `list` and `find` commands for `Menu` objects [\#177](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/177) +* Completed `edit` and `find` commands for `Order` objects [\#134](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/134) +* Contributed to `Calculate Menu Item Profit` feature [\#177](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/177) + * Justification: Allow users to view their profit for each item on the menu.This sets up the foundation for the `Homepage Profit` feature that allows to view total profit from all orders. +* Maintain code quality - Refactor methods to obey OOP principles [\#281](https://github.com/AY2223S2-CS2103T-W15-2/tp/issues/281) +* Improve test coverage and fixed test cases. [\#288](https://github.com/AY2223S2-CS2103T-W15-2/tp/issues/288) [\#140](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/140) +* Updated and fixed `find_order` related bug [\#291](https://github.com/AY2223S2-CS2103T-W15-2/tp/issues/291) and `Menu` related bugs [\#235](https://github.com/AY2223S2-CS2103T-W15-2/tp/issues/235) [\#188](https://github.com/AY2223S2-CS2103T-W15-2/tp/issues/188) +* Contributed to the demo video. + +* **New Feature**: Implemented a `Calculate Menu Item Profit` feature to get profit from item cost and item selling price. + * What it does: + * Allows users to see the profit of their item after adding their menu item to the menu. + * Justification: This feature allows home business owners to see their profit for each menu item at one glance.This feature allows the total profit seen on the homepage to work as expected. + * Highlights: Users are able to see the profit from each menu item without calculating manually. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=changgittyhub&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) +* All _reused_ code are adapted from the [AB3](https://github.com/nus-cs2103-AY2223S2/tp) code base (both functional and test code). + * The code could be refractored out to be a more standard format for repeated use. + +* **Project management**: + * Managed project documentation (UG & DG) + * Checked and updated UG to match with actual product [\#274](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/274) + * Helped to do minutes during meetings and send important information into Telegram group chat. + * Assisted in assigning Issues [\#146](https://github.com/AY2223S2-CS2103T-W15-2/tp/issues/146) [\#143](https://github.com/AY2223S2-CS2103T-W15-2/tp/issues/143) + +* **Documentation**: + * User Guide (UG): + * Drafted out documentation for some order features [\#142](https://github.com/AY2223S2-CS2103T-W15-2/tp/issues/142) and all menu item features.[\#176](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/176) + * Updated the introduction for the user guide [\#176](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/176) + * Helped reorganise the guide for commands with similar functionality [\#176](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/176) + * Fix documentation bugs for UG features to match real-life functionality.[\#168](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/168) + + * Developer Guide: + * Added and update use cases [\#31](https://github.com/AY2223S2-CS2103T-W15-2/tp/issues/31) [\#90](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/90) [\#292](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/292) + * Added and updated UML Class Diagram and documentation for Order and Menu [\#142](https://github.com/AY2223S2-CS2103T-W15-2/tp/issues/142) + * Added documentation and activity diagram for "Add" functionality features [\#168](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/168) + * Introduced a "Why we implemented this way" portion for features [\#176](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/176) + * Update Javadocs [\#287](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/287) + +* **Community**: + * PRs reviewed (with non-trivial review comments): [\#193](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/193) [\#267](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/267) + * Communicate professionally with Professor Damith on Outlook to change UG details + * Clarify questions on behalf of the team on MSTeams with Tutor. + * Communicate thoroughly with team members to reduce double work, fix bugs together when needed and raise doubt in group chat. + +* **Tools**: + * Java 11, JavaFx, Jekyll diff --git a/docs/team/chongweiguan.md b/docs/team/chongweiguan.md new file mode 100644 index 00000000000..1e8b7cfe5ad --- /dev/null +++ b/docs/team/chongweiguan.md @@ -0,0 +1,39 @@ +--- +layout: page +title : Wei Guan's Portfolio Page +--- + +### Project: AddressBook Level 3 + +Trackr is a desktop application used to keep track of order, menu, suppliers and tasks. 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. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=chognweiguan&breakdown=true) + * All _reused_ code are adapted from the [AB3](https://github.com/nus-cs2103-AY2223S2/tp) code base (both functional and test code). + * The code could be refractored out to be a more standard format for repetitive use in our project. + * The code could be adapted to fit the different models that we have. + +* **Contributions to code base**: + * Added `Order` and `Customer` Models including all field classes [\#100] (https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/100), [#107] (https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/107) + * Updated Storage related classes to allow for the storage of `Order` objects [\#114] (https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/114) + * Completed `add` and `delete` commands for `Order` objects [#120] (https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/120) + * Completed `upload csv` feature [\178] (https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/178) + * Justification: Allow users to transfer existing data from their excel sheet to trackr efficiently. + * Updated and fixed `Order` related bugs [#272] (https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/272) + +* **Review / Peer Help**: + * Helped teammates with debugging and implementation issues. + +* **Documentation**: + * User Guide: + * Documented `upload csv` feature [#185] (https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/185) + * Developer Guide: + * Updated UML Diagrams for `Logic` component [\#154] (https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/154) + * Added activity diagram for `ListCommand` [\#171] (https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/171) + +* **Community**: + * Approved a number of PRs + +* **Tools**: + * Java 11, JavaFx, JUnit diff --git a/docs/team/hmuumyatmoe.md b/docs/team/hmuumyatmoe.md new file mode 100644 index 00000000000..2a9d79e7cd7 --- /dev/null +++ b/docs/team/hmuumyatmoe.md @@ -0,0 +1,65 @@ +--- +layout: page +title: Hmuu Myat Moe's Project Portfolio Page +--- + +### Project: Trackr + +Trackr 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. + +* **Added the ability to add tasks to a task list and list tasks.** + * What it does: + * Allows users to add tasks with task description, deadline of task and completion status (DONE / NOT DONE) of task. + * Justification: This feature is made to cater to the needs of home business owners as they usually have a lot of tasks (such as ordering supplies, making deliveries) to deal with + and the app provides a convenient way to record down and get an overview of their tasks. + * Credits: Code is adapted from [AddressBook-Level3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative](https://se-education.org) +

+* **Added the ability to edit existing tasks.** + * What it does: + * Allows users to edit existing tasks so that they can keep tasks details correct and up to date. + * Justification: This feature improves the product because users may sometimes key in the wrong info + and this feature allows users to modify wrong task details easily. + * Credits: Code is adapted from [AddressBook-Level3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative](https://se-education.org) +

+* **New Feature: Added the ability to sort orders and tasks according to a given criteria.** + * What it does: + * Allows users to sort tasks or orders according to a given criteria. + * Justification: This feature improves the product significantly because home business owners has a lot of orders and tasks + and may wish to quickly find out which orders or tasks are more urgent at one glance. + This feature provides a convenient way for the home business owners to do so. + * Highlights: Users are able to sort the tasks or orders based on various criterias (Task Name, Task Deadline, Task Status, Time added, Task Status and Deadline) + * Credits: Code is adapted from [AddressBook-Level3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative](https://se-education.org) +

+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=hmuumyatmoe&breakdown=true) +
All the functional and test codes reused or adapted are reused from and adapated from [AddressBook-Level3 project](https://github.com/nus-cs2103-AY2223S2/tp) created by the [SE-EDU initiative](https://se-education.org). +
Code used for rake symbol used in Edit Command Activity Diagram in the DG is reused from [this Plant Uml forum](https://forum.plantuml.net/195/is-there-any-support-for-subactivity-or-the-rake-symbol). +

+* **Project management**: + * Managed project documentation (UG & DG) + * Checked and updated UG to match with actual product [\#276](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/276) + * Checked and standardise format of UG & DG [\#85](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/85) [\#86](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/86) + * Add and edit comments for code [\#295](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/295) + * Add test cases to improve test coverage [\#89](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/89) + * Update site-wide settings [#52](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/52) +

+* **Documentation**: + * User Guide (UG): + * Drafted out documentation for the features `tag_supplier`, `edit_supplier`, `edit_task`, `delete` and `find` + * Updated actual UG documentation for the features `add_task` and `edit_task` [\#54](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/54) + * Added UG documentation for the features `sort_tasks` and `sort_orders` [\#183](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/183), [\#193](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/193) + * Checked that UG matches with the actual product and updated UG accordingly [\#276](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/276) +

+ * Developer Guide: + * Updated Glossary to include definitions of meaningful words and phrases [\#70](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/70) + * Updated Product Scope (Target User Profile & Value Proposition) and Acknowledgements [\#57](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/57) + * Added user stories [\#57](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/57), [\#152](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/152) + * Added UML Class Diagram and updated documentation for Storage [\#152](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/152) + * Added documentation and activity diagram for Edit and Sort features [\#173](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/173), [\#286](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/286) +

+* **Community**: + * PRs reviewed (with non-trivial review comments): [\#47](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/47), [\#91](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/91) [\#154](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/154) [\#157](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/157) +* **Tools**: + * Java 11, JavaFx, Jekyll 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/liumc-sg.md b/docs/team/liumc-sg.md new file mode 100644 index 00000000000..7b8bbc0933e --- /dev/null +++ b/docs/team/liumc-sg.md @@ -0,0 +1,62 @@ +--- +layout: page +title: Liu Muchen's Project Portfolio Page +--- + +### Project: Trackr + +Trackr is a desktop tracking application used to keep track of order, menu, suppliers and tasks. 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. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=liumc-sg&breakdown=true) + +* All _reused_ code are adapted from the [AB3](https://github.com/nus-cs2103-AY2223S2/tp) code base (both functional and test code). + * The code could be refractored out to be a more standard format for repetitive use in our project (i.e. Modifying `UniquePersonList` to `UniqueItemList`). + * The code could be adapted to fit the different models that we have. (i.e. Modifying `Person` to be suitable for `Task`) + +* **Contributions to code base**: + * Enabled assertions in gradle. [\#156](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/156) + * Updated link in help command. [\#80](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/80) + * Completed `find` and `delete` command for task, together with the respective descriptor and predicate. [\#98](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/98) + * Justification: Allows the user to be able to find tasks by multiple fields instead of only the name. + * Highlights: Was used to make the predicate and command for find command for order. + * Credit: Code is adapted from [AddressBook-Level3](https://github.com/nus-cs2103-AY2223S2/tp) created by [SE-EDU initiative](https://se-education.org) + * Completed `clear` command for task. [\#102](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/102) + * Justification: Allows the user to be able to delete all tasks in one command. + * Credit: Code is adapted from [AddressBook-Level3](https://github.com/nus-cs2103-AY2223S2/tp) created by [SE-EDU initiative](https://se-education.org) + * Refactored models and commands, together with abstracting out certain components. [\#138](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/138), [\#139](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/139), [\#187](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/187) + * Justification: There were a lot of repeated code for commands (such as `add`, `edit` and `delete`) for each model (such as `Supplier` and `Task`) and . Hence, this would reduce duplicate code for similar functionality just for different models. + * Abstracted out common models (such as `Name` and `Deadline`). [\#138](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/138) + * Justification: There are repeated use of similar classes with the same functionality. Hence, this reduces duplicate code. + * Add in missing test cases. [\#283](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/283) + * Justification: Improve test coverage + +* **Contributions to the User Guide**: + * Drafted initial documentation for `tab`, `delete_task`, `delete_supplier`, `find_task`, `find_supplier`, `edit_task`. [\#47](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/47) + * Drafted initial documentation for `add_supplier`, `edit_supplier`. [\#72](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/72) + * Drafted initial documentation for `list_supplier`, `list_order`, `list_task`, `list_item`, `clear_supplier`, `clear_order`, `clear_task`, `clear_item`, `find_order`. [\#198](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/198) + * Overhaul of the structure of how the User Guide is designed and implemented. [\#267](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/267) + * Added in figures and tips to allow for better user experience. [\#267](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/267) + * Fix formatting and informational errors in the entire User Guide. [\#267](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/267) + +* **Contributions to the Developer Guide**: + * Added UML diagrams and wrote the content for `Model`, `Item`, `Person-Supplier-Customer`, `Task` and `FindXYZCommand`. [\#150](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/150) + * Updated diagrams to svg for better readability and quality, together with labelling of the diagrams. [\#157](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/157), [\#166](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/166) + * Updated target user profile, user stories and use cases. [\#166](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/166) + * Added manual test cases. [\#294](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/294) + +* **Review/Mentoring contributions**: + * Provided recommendations on how to implement respective features. + * Help teammates with issues with their code base. + +* **Project management**: + * Initialised GitHub Project, issues tracking and flow of PRs. + * Lead weekly meetings and work delegation. + * Keep track of datelines and give timely reminders. + +* **Community**: + * PRs reviewed (with non-trivial review comments): [\#89](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/89), [\#97](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/97), [\#105](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/105), [\#120](https://github.com/AY2223S2-CS2103T-W15-2/tp/pull/120) + +* **Tools**: + * Java 11, JavaFX, Grade, JUnit diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md index 880c701042f..7f8624d2804 100644 --- a/docs/tutorials/AddRemark.md +++ b/docs/tutorials/AddRemark.md @@ -25,7 +25,7 @@ For now, let’s keep `RemarkCommand` as simple as possible and print some outpu ``` java package seedu.address.logic.commands; -import seedu.address.model.Model; +import trackr.model.Model; /** * Changes the remark of an existing person in the address book. @@ -91,7 +91,7 @@ Let’s change `RemarkCommand` to parse input from the user. We start by modifying the constructor of `RemarkCommand` to accept an `Index` and a `String`. While we are at it, let’s change the error message to echo the values. While this is not a replacement for tests, it is an obvious way to tell if our code is functioning as intended. ``` java -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static trackr.commons.util.CollectionUtil.requireAllNonNull; //... public class RemarkCommand extends Command { //... @@ -242,7 +242,7 @@ Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark` Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each person. -Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688). +Simply add the following to [`trackr.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688). **`PersonCard.java`:** diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md index f29169bc924..b15f6ba682a 100644 --- a/docs/tutorials/RemovingFields.md +++ b/docs/tutorials/RemovingFields.md @@ -28,7 +28,7 @@ IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a re ### Assisted refactoring -The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. +The `address` field in `Person` is actually an instance of the `trackr.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. * :bulb: To make things simpler, you can unselect the options `Search in comments and strings` and `Search for text occurrences` ![Usages detected](../images/remove/UnsafeDelete.png) diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md index 4fb62a83ef6..bd9697218a7 100644 --- a/docs/tutorials/TracingCode.md +++ b/docs/tutorials/TracingCode.md @@ -16,11 +16,11 @@ When trying to understand an unfamiliar code base, one common strategy used is t Before we jump into the code, it is useful to get an idea of the overall structure and the high-level behavior of the application. This is provided in the 'Architecture' section of the developer guide. In particular, the architecture diagram (reproduced below), tells us that the App consists of several components. -![ArchitectureDiagram](../images/ArchitectureDiagram.png) +![ArchitectureDiagram](../images/ArchitectureDiagram.svg) It also has a sequence diagram (reproduced below) that tells us how a command propagates through the App. - + Note how the diagram shows only the execution flows _between_ the main components. That is, it does not show details of the execution path *inside* each component. By hiding those details, the diagram aims to inform the reader about the overall execution path of a command without overwhelming the reader with too much details. In this tutorial, you aim to find those omitted details so that you get a more in-depth understanding of how the code works. @@ -37,9 +37,9 @@ As you know, the first step of debugging is to put in a breakpoint where you wan In our case, we would want to begin the tracing at the very point where the App start processing user input (i.e., somewhere in the UI component), and then trace through how the execution proceeds through the UI component. However, the execution path through a GUI is often somewhat obscure due to various *event-driven mechanisms* used by GUI frameworks, which happens to be the case here too. Therefore, let us put the breakpoint where the `UI` transfers control to the `Logic` component. - + -According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `seedu.address.logic.Logic`. +According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `trackr.logic.Logic`. @@ -48,7 +48,7 @@ According to the sequence diagram you saw earlier (and repeated above for refere :bulb: **Intellij Tip:** The ['**Search Everywhere**' feature](https://www.jetbrains.com/help/idea/searching-everywhere.html) can be used here. In particular, the '**Find Symbol**' ('Symbol' here refers to methods, variables, classes etc.) variant of that feature is quite useful here as we are looking for a _method_ named `execute`, not simply the text `execute`.
-A quick look at the `seedu.address.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for. +A quick look at the `trackr.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for. ```java public interface Logic { @@ -67,7 +67,7 @@ public interface Logic { But apparently, this is an interface, not a concrete implementation. That should be fine because the [Architecture section of the Developer Guide](../DeveloperGuide.html#architecture) tells us that components interact through interfaces. Here's the relevant diagram: - + Next, let's find out which statement(s) in the `UI` code is calling this method, thus transferring control from the `UI` to the `Logic`. diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java deleted file mode 100644 index 4133aaa0151..00000000000 --- a/src/main/java/seedu/address/MainApp.java +++ /dev/null @@ -1,183 +0,0 @@ -package seedu.address; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; -import java.util.logging.Logger; - -import javafx.application.Application; -import javafx.stage.Stage; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.core.Version; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.ConfigUtil; -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.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; -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.JsonUserPrefsStorage; -import seedu.address.storage.Storage; -import seedu.address.storage.StorageManager; -import seedu.address.storage.UserPrefsStorage; -import seedu.address.ui.Ui; -import seedu.address.ui.UiManager; - -/** - * Runs the application. - */ -public class MainApp extends Application { - - public static final Version VERSION = new Version(0, 2, 0, true); - - private static final Logger logger = LogsCenter.getLogger(MainApp.class); - - protected Ui ui; - protected Logic logic; - protected Storage storage; - protected Model model; - protected Config config; - - @Override - public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); - super.init(); - - AppParameters appParameters = AppParameters.parse(getParameters()); - config = initConfig(appParameters.getConfigPath()); - - UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath()); - UserPrefs userPrefs = initPrefs(userPrefsStorage); - AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath()); - storage = new StorageManager(addressBookStorage, userPrefsStorage); - - initLogging(config); - - model = initModelManager(storage, userPrefs); - - logic = new LogicManager(model, storage); - - ui = new UiManager(logic); - } - - /** - * 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. - */ - private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; - try { - addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { - logger.info("Data file not found. Will be starting with a sample AddressBook"); - } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); - } catch (DataConversionException e) { - logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); - } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); - } - - return new ModelManager(initialData, userPrefs); - } - - private void initLogging(Config config) { - LogsCenter.init(config); - } - - /** - * Returns a {@code Config} using the file at {@code configFilePath}.
- * The default file path {@code Config#DEFAULT_CONFIG_FILE} will be used instead - * if {@code configFilePath} is null. - */ - protected Config initConfig(Path configFilePath) { - Config initializedConfig; - Path configFilePathUsed; - - configFilePathUsed = Config.DEFAULT_CONFIG_FILE; - - if (configFilePath != null) { - logger.info("Custom Config file specified " + configFilePath); - configFilePathUsed = configFilePath; - } - - logger.info("Using config file : " + configFilePathUsed); - - try { - Optional configOptional = ConfigUtil.readConfig(configFilePathUsed); - initializedConfig = configOptional.orElse(new Config()); - } catch (DataConversionException e) { - logger.warning("Config file at " + configFilePathUsed + " is not in the correct format. " - + "Using default config properties"); - initializedConfig = new Config(); - } - - //Update config file in case it was missing to begin with or there are new/unused fields - try { - ConfigUtil.saveConfig(initializedConfig, configFilePathUsed); - } catch (IOException e) { - logger.warning("Failed to save config file : " + StringUtil.getDetails(e)); - } - return initializedConfig; - } - - /** - * Returns a {@code UserPrefs} using the file at {@code storage}'s user prefs file path, - * or a new {@code UserPrefs} with default configuration if errors occur when - * reading from the file. - */ - protected UserPrefs initPrefs(UserPrefsStorage storage) { - Path prefsFilePath = storage.getUserPrefsFilePath(); - logger.info("Using prefs file : " + prefsFilePath); - - UserPrefs initializedPrefs; - try { - Optional prefsOptional = storage.readUserPrefs(); - initializedPrefs = prefsOptional.orElse(new UserPrefs()); - } catch (DataConversionException e) { - logger.warning("UserPrefs file at " + prefsFilePath + " is not in the correct format. " - + "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"); - initializedPrefs = new UserPrefs(); - } - - //Update prefs file in case it was missing to begin with or there are new/unused fields - try { - storage.saveUserPrefs(initializedPrefs); - } catch (IOException e) { - logger.warning("Failed to save config file : " + StringUtil.getDetails(e)); - } - - return initializedPrefs; - } - - @Override - public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); - ui.start(primaryStage); - } - - @Override - public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); - try { - storage.saveUserPrefs(model.getUserPrefs()); - } catch (IOException e) { - logger.severe("Failed to save preferences " + StringUtil.getDetails(e)); - } - } -} diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java deleted file mode 100644 index 1deb3a1e469..00000000000 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ /dev/null @@ -1,13 +0,0 @@ -package seedu.address.commons.core; - -/** - * Container for user visible messages. - */ -public class Messages { - - public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; - public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; - public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; - public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; - -} diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java deleted file mode 100644 index 92cd8fa605a..00000000000 --- a/src/main/java/seedu/address/logic/Logic.java +++ /dev/null @@ -1,50 +0,0 @@ -package seedu.address.logic; - -import java.nio.file.Path; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -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; - -/** - * API of the Logic component - */ -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. - */ - CommandResult execute(String commandText) throws CommandException, ParseException; - - /** - * Returns the AddressBook. - * - * @see seedu.address.model.Model#getAddressBook() - */ - ReadOnlyAddressBook getAddressBook(); - - /** Returns an unmodifiable view of the filtered list of persons */ - ObservableList getFilteredPersonList(); - - /** - * Returns the user prefs' address book file path. - */ - Path getAddressBookFilePath(); - - /** - * Returns the user prefs' GUI settings. - */ - GuiSettings getGuiSettings(); - - /** - * Set the user prefs' GUI settings. - */ - void setGuiSettings(GuiSettings guiSettings); -} diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java deleted file mode 100644 index 9d9c6d15bdc..00000000000 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ /dev/null @@ -1,81 +0,0 @@ -package seedu.address.logic; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -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.exceptions.ParseException; -import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; -import seedu.address.storage.Storage; - -/** - * The main LogicManager of the app. - */ -public class LogicManager implements Logic { - public static final String FILE_OPS_ERROR_MESSAGE = "Could not save data to file: "; - private final Logger logger = LogsCenter.getLogger(LogicManager.class); - - private final Model model; - private final Storage storage; - private final AddressBookParser addressBookParser; - - /** - * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. - */ - public LogicManager(Model model, Storage storage) { - this.model = model; - this.storage = storage; - addressBookParser = new AddressBookParser(); - } - - @Override - public CommandResult execute(String commandText) throws CommandException, ParseException { - logger.info("----------------[USER COMMAND][" + commandText + "]"); - - CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); - commandResult = command.execute(model); - - try { - storage.saveAddressBook(model.getAddressBook()); - } catch (IOException ioe) { - throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); - } - - return commandResult; - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return model.getAddressBook(); - } - - @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); - } - - @Override - public Path getAddressBookFilePath() { - return model.getAddressBookFilePath(); - } - - @Override - public GuiSettings getGuiSettings() { - return model.getGuiSettings(); - } - - @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 deleted file mode 100644 index 71656d7c5c8..00000000000 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ /dev/null @@ -1,67 +0,0 @@ -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 seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; - -/** - * Adds a person to the address book. - */ -public class AddCommand extends 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} - */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - 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/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java deleted file mode 100644 index 9c86b1fa6e4..00000000000 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ /dev/null @@ -1,23 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import seedu.address.model.AddressBook; -import seedu.address.model.Model; - -/** - * Clears the address book. - */ -public class ClearCommand extends Command { - - public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; - - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.setAddressBook(new AddressBook()); - return new CommandResult(MESSAGE_SUCCESS); - } -} diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java deleted file mode 100644 index 02fd256acba..00000000000 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ /dev/null @@ -1,53 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; - -/** - * Deletes a person identified using it's displayed index from the address book. - */ -public class DeleteCommand extends Command { - - public static final String COMMAND_WORD = "delete"; - - public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Deletes the person identified by the index number used in the displayed person list.\n" - + "Parameters: INDEX (must be a positive integer)\n" - + "Example: " + COMMAND_WORD + " 1"; - - public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; - - private final Index targetIndex; - - public DeleteCommand(Index targetIndex) { - this.targetIndex = targetIndex; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); - - if (targetIndex.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - Person personToDelete = 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/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java deleted file mode 100644 index 7e36114902f..00000000000 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ /dev/null @@ -1,226 +0,0 @@ -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 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. - */ -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; - - /** - * @param index of the person in the filtered person list to edit - * @param editPersonDescriptor details to edit the person with - */ - public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { - requireNonNull(index); - requireNonNull(editPersonDescriptor); - - this.index = index; - 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)); - } - - /** - * Creates and returns a {@code Person} with the details of {@code personToEdit} - * edited with {@code editPersonDescriptor}. - */ - private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { - assert personToEdit != null; - - 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); - } - - /** - * Stores the details to edit the person with. Each non-empty field value will replace the - * corresponding field value of the person. - */ - 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; - - 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/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java deleted file mode 100644 index d6b19b0a0de..00000000000 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ /dev/null @@ -1,42 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import seedu.address.commons.core.Messages; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -/** - * Finds and lists all persons in address book whose name contains any of the argument keywords. - * Keyword matching is case insensitive. - */ -public class FindCommand extends Command { - - public static final String COMMAND_WORD = "find"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of " - + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" - + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" - + "Example: " + COMMAND_WORD + " alice bob charlie"; - - private final NameContainsKeywordsPredicate predicate; - - public FindCommand(NameContainsKeywordsPredicate predicate) { - this.predicate = predicate; - } - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.updateFilteredPersonList(predicate); - return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof FindCommand // instanceof handles nulls - && predicate.equals(((FindCommand) other).predicate)); // state check - } -} diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java deleted file mode 100644 index 84be6ad2596..00000000000 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ /dev/null @@ -1,24 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; - -import seedu.address.model.Model; - -/** - * Lists all persons in the address book to the user. - */ -public class ListCommand extends Command { - - public static final String COMMAND_WORD = "list"; - - public static final String MESSAGE_SUCCESS = "Listed all persons"; - - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(MESSAGE_SUCCESS); - } -} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java deleted file mode 100644 index 3b8bfa035e8..00000000000 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ /dev/null @@ -1,60 +0,0 @@ -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 - */ -public class AddCommandParser implements Parser { - - /** - * 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}. - */ - 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/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java deleted file mode 100644 index 75b1a9bf119..00000000000 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ /dev/null @@ -1,15 +0,0 @@ -package seedu.address.logic.parser; - -/** - * 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/"); - -} diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java deleted file mode 100644 index 522b93081cc..00000000000 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ /dev/null @@ -1,29 +0,0 @@ -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 - */ -public class DeleteCommandParser implements Parser { - - /** - * 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 - */ - 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); - } - } - -} diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java deleted file mode 100644 index 845644b7dea..00000000000 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ /dev/null @@ -1,82 +0,0 @@ -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 - */ -public class EditCommandParser implements Parser { - - /** - * 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. - */ - 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/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java deleted file mode 100644 index 4fb71f23103..00000000000 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ /dev/null @@ -1,33 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; - -import java.util.Arrays; - -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -/** - * Parses input arguments and creates a new FindCommand object - */ -public class FindCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the FindCommand - * and returns a FindCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public FindCommand parse(String args) throws ParseException { - String trimmedArgs = args.trim(); - if (trimmedArgs.isEmpty()) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); - } - - String[] nameKeywords = trimmedArgs.split("\\s+"); - - return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); - } - -} diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java deleted file mode 100644 index b117acb9c55..00000000000 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ /dev/null @@ -1,124 +0,0 @@ -package seedu.address.logic.parser; - -import static java.util.Objects.requireNonNull; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Contains utility methods used for parsing strings in the various *Parser classes. - */ -public class ParserUtil { - - public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; - - /** - * 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 { - String trimmedIndex = oneBasedIndex.trim(); - if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { - throw new ParseException(MESSAGE_INVALID_INDEX); - } - return Index.fromOneBased(Integer.parseInt(trimmedIndex)); - } - - /** - * Parses a {@code String name} into a {@code Name}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code name} is invalid. - */ - public static Name parseName(String name) throws ParseException { - requireNonNull(name); - String trimmedName = name.trim(); - if (!Name.isValidName(trimmedName)) { - throw new ParseException(Name.MESSAGE_CONSTRAINTS); - } - return new Name(trimmedName); - } - - /** - * Parses a {@code String phone} into a {@code Phone}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code phone} is invalid. - */ - public static Phone parsePhone(String phone) throws ParseException { - requireNonNull(phone); - String trimmedPhone = phone.trim(); - if (!Phone.isValidPhone(trimmedPhone)) { - throw new ParseException(Phone.MESSAGE_CONSTRAINTS); - } - return new Phone(trimmedPhone); - } - - /** - * Parses a {@code String address} into an {@code Address}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code address} is invalid. - */ - public static Address parseAddress(String address) throws ParseException { - requireNonNull(address); - String trimmedAddress = address.trim(); - if (!Address.isValidAddress(trimmedAddress)) { - throw new ParseException(Address.MESSAGE_CONSTRAINTS); - } - return new Address(trimmedAddress); - } - - /** - * Parses a {@code String email} into an {@code Email}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code email} is invalid. - */ - public static Email parseEmail(String email) throws ParseException { - requireNonNull(email); - String trimmedEmail = email.trim(); - if (!Email.isValidEmail(trimmedEmail)) { - throw new ParseException(Email.MESSAGE_CONSTRAINTS); - } - return new Email(trimmedEmail); - } - - /** - * Parses a {@code String tag} into a {@code Tag}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code tag} is invalid. - */ - public static Tag parseTag(String tag) throws ParseException { - requireNonNull(tag); - String trimmedTag = tag.trim(); - if (!Tag.isValidTagName(trimmedTag)) { - throw new ParseException(Tag.MESSAGE_CONSTRAINTS); - } - return new Tag(trimmedTag); - } - - /** - * Parses {@code Collection tags} into a {@code Set}. - */ - public static Set parseTags(Collection tags) throws ParseException { - requireNonNull(tags); - final Set tagSet = new HashSet<>(); - for (String tagName : tags) { - tagSet.add(parseTag(tagName)); - } - return tagSet; - } -} 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/Model.java b/src/main/java/seedu/address/model/Model.java deleted file mode 100644 index d54df471c1f..00000000000 --- a/src/main/java/seedu/address/model/Model.java +++ /dev/null @@ -1,87 +0,0 @@ -package seedu.address.model; - -import java.nio.file.Path; -import java.util.function.Predicate; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.Person; - -/** - * The API of the Model component. - */ -public interface Model { - /** {@code Predicate} that always evaluate to true */ - Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; - - /** - * Replaces user prefs data with the data in {@code userPrefs}. - */ - void setUserPrefs(ReadOnlyUserPrefs userPrefs); - - /** - * Returns the user prefs. - */ - ReadOnlyUserPrefs getUserPrefs(); - - /** - * Returns the user prefs' GUI settings. - */ - GuiSettings getGuiSettings(); - - /** - * Sets the user prefs' GUI settings. - */ - void setGuiSettings(GuiSettings guiSettings); - - /** - * Returns the user prefs' address book file path. - */ - Path getAddressBookFilePath(); - - /** - * Sets the user prefs' address book file path. - */ - void setAddressBookFilePath(Path addressBookFilePath); - - /** - * Replaces address book data with the data in {@code addressBook}. - */ - void setAddressBook(ReadOnlyAddressBook addressBook); - - /** Returns the AddressBook */ - ReadOnlyAddressBook getAddressBook(); - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - boolean hasPerson(Person person); - - /** - * Deletes the given person. - * The person must exist in the address book. - */ - void deletePerson(Person target); - - /** - * Adds the given person. - * {@code person} must not already exist in the address book. - */ - void addPerson(Person person); - - /** - * 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. - */ - void setPerson(Person target, Person editedPerson); - - /** Returns an unmodifiable view of the filtered person list */ - ObservableList getFilteredPersonList(); - - /** - * Updates the filter of the filtered person list to filter by the given {@code predicate}. - * @throws NullPointerException if {@code predicate} is null. - */ - void updateFilteredPersonList(Predicate predicate); -} diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java deleted file mode 100644 index 86c1df298d7..00000000000 --- a/src/main/java/seedu/address/model/ModelManager.java +++ /dev/null @@ -1,150 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.nio.file.Path; -import java.util.function.Predicate; -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import javafx.collections.transformation.FilteredList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; - -/** - * Represents the in-memory model of the address book data. - */ -public class ModelManager implements Model { - private static final Logger logger = LogsCenter.getLogger(ModelManager.class); - - private final AddressBook addressBook; - private final UserPrefs userPrefs; - private final FilteredList filteredPersons; - - /** - * Initializes a ModelManager with the given addressBook and userPrefs. - */ - public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { - requireAllNonNull(addressBook, userPrefs); - - logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); - - this.addressBook = new AddressBook(addressBook); - this.userPrefs = new UserPrefs(userPrefs); - filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); - } - - public ModelManager() { - this(new AddressBook(), new UserPrefs()); - } - - //=========== UserPrefs ================================================================================== - - @Override - public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { - requireNonNull(userPrefs); - this.userPrefs.resetData(userPrefs); - } - - @Override - public ReadOnlyUserPrefs getUserPrefs() { - return userPrefs; - } - - @Override - public GuiSettings getGuiSettings() { - return userPrefs.getGuiSettings(); - } - - @Override - public void setGuiSettings(GuiSettings guiSettings) { - requireNonNull(guiSettings); - userPrefs.setGuiSettings(guiSettings); - } - - @Override - public Path getAddressBookFilePath() { - return userPrefs.getAddressBookFilePath(); - } - - @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - userPrefs.setAddressBookFilePath(addressBookFilePath); - } - - //=========== AddressBook ================================================================================ - - @Override - public void setAddressBook(ReadOnlyAddressBook addressBook) { - this.addressBook.resetData(addressBook); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return addressBook; - } - - @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return addressBook.hasPerson(person); - } - - @Override - public void deletePerson(Person target) { - addressBook.removePerson(target); - } - - @Override - public void addPerson(Person person) { - addressBook.addPerson(person); - updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - } - - @Override - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - addressBook.setPerson(target, editedPerson); - } - - //=========== Filtered Person List Accessors ============================================================= - - /** - * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of - * {@code versionedAddressBook} - */ - @Override - public ObservableList getFilteredPersonList() { - return filteredPersons; - } - - @Override - public void updateFilteredPersonList(Predicate predicate) { - requireNonNull(predicate); - filteredPersons.setPredicate(predicate); - } - - @Override - public boolean equals(Object obj) { - // short circuit if same object - if (obj == this) { - return true; - } - - // instanceof handles nulls - if (!(obj instanceof ModelManager)) { - return false; - } - - // state check - ModelManager other = (ModelManager) obj; - return addressBook.equals(other.addressBook) - && userPrefs.equals(other.userPrefs) - && filteredPersons.equals(other.filteredPersons); - } - -} 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/person/Address.java b/src/main/java/seedu/address/model/person/Address.java deleted file mode 100644 index 60472ca22a0..00000000000 --- a/src/main/java/seedu/address/model/person/Address.java +++ /dev/null @@ -1,57 +0,0 @@ -package seedu.address.model.person; - -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)} - */ -public class Address { - - 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, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[^\\s].*"; - - public final String value; - - /** - * Constructs an {@code Address}. - * - * @param address A valid address. - */ - public Address(String address) { - requireNonNull(address); - checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS); - value = address; - } - - /** - * Returns true if a given string is a valid email. - */ - public static boolean isValidAddress(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Address // instanceof handles nulls - && value.equals(((Address) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/person/Name.java deleted file mode 100644 index 79244d71cf7..00000000000 --- a/src/main/java/seedu/address/model/person/Name.java +++ /dev/null @@ -1,59 +0,0 @@ -package seedu.address.model.person; - -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)} - */ -public class 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, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; - - public final String fullName; - - /** - * Constructs a {@code Name}. - * - * @param name A valid name. - */ - public Name(String name) { - requireNonNull(name); - checkArgument(isValidName(name), MESSAGE_CONSTRAINTS); - fullName = name; - } - - /** - * Returns true if a given string is a valid name. - */ - public static boolean isValidName(String test) { - return test.matches(VALIDATION_REGEX); - } - - - @Override - public String toString() { - return fullName; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Name // instanceof handles nulls - && fullName.equals(((Name) other).fullName)); // state check - } - - @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/person/NameContainsKeywordsPredicate.java deleted file mode 100644 index c9b5868427c..00000000000 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ /dev/null @@ -1,31 +0,0 @@ -package seedu.address.model.person; - -import java.util.List; -import java.util.function.Predicate; - -import seedu.address.commons.util.StringUtil; - -/** - * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. - */ -public class NameContainsKeywordsPredicate implements Predicate { - private final List keywords; - - public NameContainsKeywordsPredicate(List keywords) { - this.keywords = keywords; - } - - @Override - public boolean test(Person person) { - return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof NameContainsKeywordsPredicate // instanceof handles nulls - && keywords.equals(((NameContainsKeywordsPredicate) other).keywords)); // state check - } - -} 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/Phone.java b/src/main/java/seedu/address/model/person/Phone.java deleted file mode 100644 index 872c76b382f..00000000000 --- a/src/main/java/seedu/address/model/person/Phone.java +++ /dev/null @@ -1,53 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's phone number in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} - */ -public class Phone { - - - public static final String MESSAGE_CONSTRAINTS = - "Phone numbers should only contain numbers, and it should be at least 3 digits long"; - public static final String VALIDATION_REGEX = "\\d{3,}"; - public final String value; - - /** - * Constructs a {@code Phone}. - * - * @param phone A valid phone number. - */ - public Phone(String phone) { - requireNonNull(phone); - checkArgument(isValidPhone(phone), MESSAGE_CONSTRAINTS); - value = phone; - } - - /** - * Returns true if a given string is a valid phone number. - */ - public static boolean isValidPhone(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Phone // instanceof handles nulls - && value.equals(((Phone) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} 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/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java deleted file mode 100644 index 1806da4facf..00000000000 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.model.util; - -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; - -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Contains utility methods for populating {@code AddressBook} with sample data. - */ -public class SampleDataUtil { - public static Person[] getSamplePersons() { - return new Person[] { - new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends")), - new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends")), - new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours")), - new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family")), - new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates")), - new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues")) - }; - } - - public static ReadOnlyAddressBook getSampleAddressBook() { - AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); - } - return sampleAb; - } - - /** - * Returns a tag set containing the list of strings given. - */ - public static Set getTagSet(String... strings) { - return Arrays.stream(strings) - .map(Tag::new) - .collect(Collectors.toSet()); - } - -} 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/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/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/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/Storage.java b/src/main/java/seedu/address/storage/Storage.java deleted file mode 100644 index beda8bd9f11..00000000000 --- a/src/main/java/seedu/address/storage/Storage.java +++ /dev/null @@ -1,32 +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; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; - -/** - * API of the Storage component - */ -public interface Storage extends AddressBookStorage, UserPrefsStorage { - - @Override - Optional readUserPrefs() throws DataConversionException, IOException; - - @Override - void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; - - @Override - Path getAddressBookFilePath(); - - @Override - Optional readAddressBook() throws DataConversionException, IOException; - - @Override - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - -} diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java deleted file mode 100644 index 6cfa0162164..00000000000 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ /dev/null @@ -1,78 +0,0 @@ -package seedu.address.storage; - -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.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; - -/** - * Manages storage of AddressBook data in local storage. - */ -public class StorageManager implements Storage { - - private static final Logger logger = LogsCenter.getLogger(StorageManager.class); - private AddressBookStorage addressBookStorage; - private UserPrefsStorage userPrefsStorage; - - /** - * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}. - */ - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { - this.addressBookStorage = addressBookStorage; - this.userPrefsStorage = userPrefsStorage; - } - - // ================ UserPrefs methods ============================== - - @Override - public Path getUserPrefsFilePath() { - return userPrefsStorage.getUserPrefsFilePath(); - } - - @Override - public Optional readUserPrefs() throws DataConversionException, IOException { - return userPrefsStorage.readUserPrefs(); - } - - @Override - public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { - userPrefsStorage.saveUserPrefs(userPrefs); - } - - - // ================ AddressBook methods ============================== - - @Override - public Path getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); - } - - @Override - public Optional readAddressBook() throws DataConversionException, IOException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); - } - - @Override - public Optional readAddressBook(Path filePath) throws DataConversionException, IOException { - logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - logger.fine("Attempting to write to data file: " + filePath); - addressBookStorage.saveAddressBook(addressBook, filePath); - } - -} 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/AppParameters.java b/src/main/java/trackr/AppParameters.java similarity index 93% rename from src/main/java/seedu/address/AppParameters.java rename to src/main/java/trackr/AppParameters.java index ab552c398f3..75540cee1ec 100644 --- a/src/main/java/seedu/address/AppParameters.java +++ b/src/main/java/trackr/AppParameters.java @@ -1,4 +1,4 @@ -package seedu.address; +package trackr; import java.nio.file.Path; import java.nio.file.Paths; @@ -7,8 +7,8 @@ import java.util.logging.Logger; import javafx.application.Application; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.FileUtil; +import trackr.commons.core.LogsCenter; +import trackr.commons.util.FileUtil; /** * Represents the parsed command-line parameters given to the application. diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/trackr/Main.java similarity index 97% rename from src/main/java/seedu/address/Main.java rename to src/main/java/trackr/Main.java index 052a5068631..70382adb42c 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/trackr/Main.java @@ -1,4 +1,4 @@ -package seedu.address; +package trackr; import javafx.application.Application; diff --git a/src/main/java/trackr/MainApp.java b/src/main/java/trackr/MainApp.java new file mode 100644 index 00000000000..803c3713944 --- /dev/null +++ b/src/main/java/trackr/MainApp.java @@ -0,0 +1,245 @@ +package trackr; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import javafx.application.Application; +import javafx.stage.Stage; +import trackr.commons.core.Config; +import trackr.commons.core.LogsCenter; +import trackr.commons.core.Version; +import trackr.commons.exceptions.DataConversionException; +import trackr.commons.util.ConfigUtil; +import trackr.commons.util.StringUtil; +import trackr.logic.Logic; +import trackr.logic.LogicManager; +import trackr.model.Menu; +import trackr.model.Model; +import trackr.model.ModelManager; +import trackr.model.OrderList; +import trackr.model.ReadOnlyMenu; +import trackr.model.ReadOnlyOrderList; +import trackr.model.ReadOnlySupplierList; +import trackr.model.ReadOnlyTaskList; +import trackr.model.ReadOnlyUserPrefs; +import trackr.model.SupplierList; +import trackr.model.TaskList; +import trackr.model.UserPrefs; +import trackr.model.util.SampleDataUtil; +import trackr.storage.JsonTrackrStorage; +import trackr.storage.JsonUserPrefsStorage; +import trackr.storage.Storage; +import trackr.storage.StorageManager; +import trackr.storage.TrackrStorage; +import trackr.storage.UserPrefsStorage; +import trackr.ui.Ui; +import trackr.ui.UiManager; + +/** + * Runs the application. + */ +public class MainApp extends Application { + + public static final Version VERSION = new Version(0, 2, 0, true); + + private static final Logger logger = LogsCenter.getLogger(MainApp.class); + + protected Ui ui; + protected Logic logic; + protected Storage storage; + protected Model model; + protected Config config; + + @Override + public void init() throws Exception { + logger.info("=============================[ Initializing Trackr ]==========================="); + super.init(); + + AppParameters appParameters = AppParameters.parse(getParameters()); + config = initConfig(appParameters.getConfigPath()); + + UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath()); + UserPrefs userPrefs = initPrefs(userPrefsStorage); + TrackrStorage trackrStorage = new JsonTrackrStorage(userPrefs.getTrackrFilePath()); + storage = new StorageManager(trackrStorage, userPrefsStorage); + + initLogging(config); + + model = initModelManager(storage, userPrefs); + + logic = new LogicManager(model, storage); + + ui = new UiManager(logic); + } + + /** + * Returns a {@code ModelManager} with the data from {@code storage}'s supplier list, + * task list and {@code userPrefs}.
+ * The data from the sample supplier list and task list will be used instead if {@code storage}'s supplier list + * and task list is not found, + * or an empty supplier list and task list will be used instead if errors occur when reading {@code storage}'s + * supplier list and task list. + */ + private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { + Optional supplierListOptional; + Optional taskListOptional; + Optional menuOptional; + Optional orderListOptional; + ReadOnlySupplierList initialSupplierList; + ReadOnlyTaskList initialTaskList; + ReadOnlyMenu initialMenu; + ReadOnlyOrderList initialOrderList; + + try { + supplierListOptional = storage.readSupplierList(); + if (!supplierListOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample SupplierList"); + } + initialSupplierList = supplierListOptional.orElseGet(SampleDataUtil::getSampleSupplierList); + } catch (DataConversionException e) { + logger.warning("Data file not in the correct format. Will be starting with an empty SupplierList"); + initialSupplierList = new SupplierList(); + } catch (IOException e) { + logger.warning("Problem while reading from the file. Will be starting with an empty SupplierList"); + initialSupplierList = new SupplierList(); + } + + //@@author liumc-sg-reused + try { + taskListOptional = storage.readTaskList(); + if (!taskListOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample TaskList"); + } + initialTaskList = taskListOptional.orElseGet(SampleDataUtil::getSampleTaskList); + } catch (DataConversionException e) { + logger.warning("Data file not in the correct format. Will be starting with an empty TaskList"); + initialTaskList = new TaskList(); + } catch (IOException e) { + logger.warning("Problem while reading from the file. Will be starting with an empty TaskList"); + initialTaskList = new TaskList(); + } + //@@author + + try { + menuOptional = storage.readMenu(); + if (!menuOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample Menu"); + } + initialMenu = menuOptional.orElseGet(SampleDataUtil::getSampleMenu); + } catch (DataConversionException e) { + logger.warning("Data file not in the correct format. Will be starting with an empty Menu"); + initialMenu = new Menu(); + } catch (IOException e) { + logger.warning("Problem while reading from the file. Will be starting with an empty Menu"); + initialMenu = new Menu(); + } + + //@@author chongweiguan-reused + try { + orderListOptional = storage.readOrderList(); + if (!orderListOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample OrderList"); + } + initialOrderList = orderListOptional.orElseGet(SampleDataUtil::getSampleOrderList); + } catch (DataConversionException e) { + logger.warning("Data file not in the correct format. Will be starting with an empty OrderList"); + initialOrderList = new OrderList(); + } catch (IOException e) { + logger.warning("Problem while reading from the file. Will be starting with an empty OrderList"); + initialOrderList = new OrderList(); + } + //@@author + + return new ModelManager(initialSupplierList, initialTaskList, initialMenu, initialOrderList, userPrefs); + } + + private void initLogging(Config config) { + LogsCenter.init(config); + } + + /** + * Returns a {@code Config} using the file at {@code configFilePath}.
+ * The default file path {@code Config#DEFAULT_CONFIG_FILE} will be used instead + * if {@code configFilePath} is null. + */ + protected Config initConfig(Path configFilePath) { + Config initializedConfig; + Path configFilePathUsed; + + configFilePathUsed = Config.DEFAULT_CONFIG_FILE; + + if (configFilePath != null) { + logger.info("Custom Config file specified " + configFilePath); + configFilePathUsed = configFilePath; + } + + logger.info("Using config file : " + configFilePathUsed); + + try { + Optional configOptional = ConfigUtil.readConfig(configFilePathUsed); + initializedConfig = configOptional.orElse(new Config()); + } catch (DataConversionException e) { + logger.warning("Config file at " + configFilePathUsed + " is not in the correct format. " + + "Using default config properties"); + initializedConfig = new Config(); + } + + //Update config file in case it was missing to begin with or there are new/unused fields + try { + ConfigUtil.saveConfig(initializedConfig, configFilePathUsed); + } catch (IOException e) { + logger.warning("Failed to save config file : " + StringUtil.getDetails(e)); + } + return initializedConfig; + } + + /** + * Returns a {@code UserPrefs} using the file at {@code storage}'s user prefs file path, + * or a new {@code UserPrefs} with default configuration if errors occur when + * reading from the file. + */ + protected UserPrefs initPrefs(UserPrefsStorage storage) { + Path prefsFilePath = storage.getUserPrefsFilePath(); + logger.info("Using prefs file : " + prefsFilePath); + + UserPrefs initializedPrefs; + try { + Optional prefsOptional = storage.readUserPrefs(); + initializedPrefs = prefsOptional.orElse(new UserPrefs()); + } catch (DataConversionException e) { + logger.warning("UserPrefs file at " + prefsFilePath + " is not in the correct format. " + + "Using default user prefs"); + initializedPrefs = new UserPrefs(); + } catch (IOException e) { + logger.warning("Problem while reading from the file. Will be starting with an empty Trackr"); + initializedPrefs = new UserPrefs(); + } + + //Update prefs file in case it was missing to begin with or there are new/unused fields + try { + storage.saveUserPrefs(initializedPrefs); + } catch (IOException e) { + logger.warning("Failed to save config file : " + StringUtil.getDetails(e)); + } + + return initializedPrefs; + } + + @Override + public void start(Stage primaryStage) { + logger.info("Starting Trackr " + MainApp.VERSION); + ui.start(primaryStage); + } + + @Override + public void stop() { + logger.info("============================ [ Stopping Trackr ] ============================="); + try { + storage.saveUserPrefs(model.getUserPrefs()); + } catch (IOException e) { + logger.severe("Failed to save preferences " + StringUtil.getDetails(e)); + } + } +} diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/trackr/commons/core/Config.java similarity index 97% rename from src/main/java/seedu/address/commons/core/Config.java rename to src/main/java/trackr/commons/core/Config.java index 91145745521..97ecf8b670c 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/trackr/commons/core/Config.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package trackr.commons.core; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/trackr/commons/core/GuiSettings.java similarity index 96% rename from src/main/java/seedu/address/commons/core/GuiSettings.java rename to src/main/java/trackr/commons/core/GuiSettings.java index ba33653be67..1b1c31ce11d 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/trackr/commons/core/GuiSettings.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package trackr.commons.core; import java.awt.Point; import java.io.Serializable; @@ -11,7 +11,7 @@ public class GuiSettings implements Serializable { private static final double DEFAULT_HEIGHT = 600; - private static final double DEFAULT_WIDTH = 740; + private static final double DEFAULT_WIDTH = 700; private final double windowWidth; private final double windowHeight; diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/trackr/commons/core/LogsCenter.java similarity index 97% rename from src/main/java/seedu/address/commons/core/LogsCenter.java rename to src/main/java/trackr/commons/core/LogsCenter.java index 431e7185e76..c3045487e72 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/trackr/commons/core/LogsCenter.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package trackr.commons.core; import java.io.IOException; import java.util.Arrays; @@ -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 = "trackr.log"; private static Level currentLogLevel = Level.INFO; private static final Logger logger = LogsCenter.getLogger(LogsCenter.class); private static FileHandler fileHandler; diff --git a/src/main/java/trackr/commons/core/Messages.java b/src/main/java/trackr/commons/core/Messages.java new file mode 100644 index 00000000000..207feb4f595 --- /dev/null +++ b/src/main/java/trackr/commons/core/Messages.java @@ -0,0 +1,21 @@ +package trackr.commons.core; + +/** + * Container for user visible messages. + */ +public class Messages { + + public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; + public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; + public static final String MESSAGE_INVALID_ITEM_DISPLAYED_INDEX = "The %s index provided is invalid"; + public static final String MESSAGE_ITEMS_LISTED_OVERVIEW = "%d %ss listed!"; + public static final String MESSAGE_INVALID_SUPPLIER_DISPLAYED_INDEX = "The supplier index provided is invalid"; + public static final String MESSAGE_INVALID_TASK_DISPLAYED_INDEX = "The task index provided is invalid"; + public static final String MESSAGE_TASKS_LISTED_OVERVIEW = "%1$d tasks listed!"; + public static final String MESSAGE_INVALID_ORDER_DISPLAYED_INDEX = "The order index provided is invalid"; + public static final String MESSAGE_ORDERS_LISTED_OVERVIEW = "%1$d orders listed!"; + public static final String MESSAGE_INVALID_CSV_FILE = "Invalid csv file"; + public static final String MESSAGE_NO_MENU_ITEM = "No such item in your menu."; + public static final String MESSAGE_DUPLICATE_ITEM = "This %s already exists in the %s list"; + public static final String MESSAGE_SUCCESS = "New %s added: %s"; +} diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/trackr/commons/core/Version.java similarity index 98% rename from src/main/java/seedu/address/commons/core/Version.java rename to src/main/java/trackr/commons/core/Version.java index 12142ec1e32..9f39777e1f6 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/trackr/commons/core/Version.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package trackr.commons.core; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/trackr/commons/core/index/Index.java similarity index 97% rename from src/main/java/seedu/address/commons/core/index/Index.java rename to src/main/java/trackr/commons/core/index/Index.java index 19536439c09..db33aa031a9 100644 --- a/src/main/java/seedu/address/commons/core/index/Index.java +++ b/src/main/java/trackr/commons/core/index/Index.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core.index; +package trackr.commons.core.index; /** * Represents a zero-based or one-based index. diff --git a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java b/src/main/java/trackr/commons/exceptions/DataConversionException.java similarity index 84% rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java rename to src/main/java/trackr/commons/exceptions/DataConversionException.java index 1f689bd8e3f..4a0a7fbca60 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java +++ b/src/main/java/trackr/commons/exceptions/DataConversionException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package trackr.commons.exceptions; /** * Represents an error during conversion of data from one format to another diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/trackr/commons/exceptions/IllegalValueException.java similarity index 93% rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java rename to src/main/java/trackr/commons/exceptions/IllegalValueException.java index 19124db485c..a85c6e971de 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/trackr/commons/exceptions/IllegalValueException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package trackr.commons.exceptions; /** * Signals that some given data does not fulfill some constraints. diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/trackr/commons/util/AppUtil.java similarity index 94% rename from src/main/java/seedu/address/commons/util/AppUtil.java rename to src/main/java/trackr/commons/util/AppUtil.java index 87aa89c0326..aa9c4f4d278 100644 --- a/src/main/java/seedu/address/commons/util/AppUtil.java +++ b/src/main/java/trackr/commons/util/AppUtil.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package trackr.commons.util; import static java.util.Objects.requireNonNull; import javafx.scene.image.Image; -import seedu.address.MainApp; +import trackr.MainApp; /** * A container for App specific utility functions diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/trackr/commons/util/CollectionUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/CollectionUtil.java rename to src/main/java/trackr/commons/util/CollectionUtil.java index eafe4dfd681..5b9b030cb1d 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/trackr/commons/util/CollectionUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package trackr.commons.util; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/trackr/commons/util/ConfigUtil.java similarity index 77% rename from src/main/java/seedu/address/commons/util/ConfigUtil.java rename to src/main/java/trackr/commons/util/ConfigUtil.java index f7f8a2bd44c..d22b5d57035 100644 --- a/src/main/java/seedu/address/commons/util/ConfigUtil.java +++ b/src/main/java/trackr/commons/util/ConfigUtil.java @@ -1,11 +1,11 @@ -package seedu.address.commons.util; +package trackr.commons.util; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; +import trackr.commons.core.Config; +import trackr.commons.exceptions.DataConversionException; /** * A class for accessing the Config File. diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/trackr/commons/util/FileUtil.java similarity index 98% rename from src/main/java/seedu/address/commons/util/FileUtil.java rename to src/main/java/trackr/commons/util/FileUtil.java index b1e2767cdd9..cd173d050d2 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/trackr/commons/util/FileUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package trackr.commons.util; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/trackr/commons/util/JsonUtil.java similarity index 97% rename from src/main/java/seedu/address/commons/util/JsonUtil.java rename to src/main/java/trackr/commons/util/JsonUtil.java index 8ef609f055d..6b2e758a45e 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/trackr/commons/util/JsonUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package trackr.commons.util; import static java.util.Objects.requireNonNull; @@ -20,8 +20,8 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; +import trackr.commons.core.LogsCenter; +import trackr.commons.exceptions.DataConversionException; /** * Converts a Java object instance to JSON and vice versa diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/trackr/commons/util/StringUtil.java similarity index 95% rename from src/main/java/seedu/address/commons/util/StringUtil.java rename to src/main/java/trackr/commons/util/StringUtil.java index 61cc8c9a1cb..ec7d81d68bf 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/trackr/commons/util/StringUtil.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package trackr.commons.util; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static trackr.commons.util.AppUtil.checkArgument; import java.io.PrintWriter; import java.io.StringWriter; diff --git a/src/main/java/trackr/logic/Logic.java b/src/main/java/trackr/logic/Logic.java new file mode 100644 index 00000000000..05266d7f08e --- /dev/null +++ b/src/main/java/trackr/logic/Logic.java @@ -0,0 +1,114 @@ +package trackr.logic; + +import java.nio.file.Path; + +import javafx.collections.ObservableList; +import trackr.commons.core.GuiSettings; +import trackr.logic.commands.CommandResult; +import trackr.logic.commands.exceptions.CommandException; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.ReadOnlyMenu; +import trackr.model.ReadOnlyOrderList; +import trackr.model.ReadOnlySupplierList; +import trackr.model.ReadOnlyTaskList; +import trackr.model.menu.ItemProfit; +import trackr.model.menu.ItemSellingPrice; +import trackr.model.menu.MenuItem; +import trackr.model.order.Order; +import trackr.model.person.Supplier; +import trackr.model.task.Task; + +/** + * API of the Logic component. + */ +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. + */ + CommandResult execute(String commandText) throws CommandException, ParseException; + + /** + * Returns the SupplierList. + * + * @see trackr.model.Model#getSupplierList() + */ + ReadOnlySupplierList getSupplierList(); + + /** + * Returns an unmodifiable view of the filtered list of suppliers. + */ + ObservableList getFilteredSupplierList(); + + //@@author chongweiguan-reused + /** + * Returns the OrderList + * + * @see trackr.model.Model#getOrderList() + */ + ReadOnlyOrderList getOrderList(); + + /** + * Returns an unmodifiable view of the filtered list of orders. + */ + ObservableList getFilteredOrderList(); + //@@author + + /** + * Returns cumulative profits. + */ + ItemProfit getTotalProfits(); + + /** + * Returns cumulative sales/ revenue. + */ + ItemSellingPrice getTotalSales(); + + //@@author liumc-sg-reused + /** + * Returns the TaskList. + * + * @see trackr.model.Model#getTaskList() + */ + ReadOnlyTaskList getTaskList(); + //@@author + + /** + * Returns an unmodifiable view of the filtered list of tasks. + */ + //@@author liumc-sg-reused + ObservableList getFilteredTaskList(); + //@@author + + /** + * Returns the Menu. + * + * @see trackr.model.Model#getMenu() + */ + ReadOnlyMenu getMenu(); + + /** + * Returns an unmodifiable view of the filtered list of menu items. + */ + ObservableList getFilteredMenu(); + + + /** + * Returns the user prefs' trackr file path. + */ + Path getTrackrFilePath(); + + /** + * Returns the user prefs' GUI settings. + */ + GuiSettings getGuiSettings(); + + /** + * Set the user prefs' GUI settings. + */ + void setGuiSettings(GuiSettings guiSettings); +} diff --git a/src/main/java/trackr/logic/LogicManager.java b/src/main/java/trackr/logic/LogicManager.java new file mode 100644 index 00000000000..cf86c46dade --- /dev/null +++ b/src/main/java/trackr/logic/LogicManager.java @@ -0,0 +1,146 @@ +package trackr.logic; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import trackr.commons.core.GuiSettings; +import trackr.commons.core.LogsCenter; +import trackr.logic.commands.Command; +import trackr.logic.commands.CommandResult; +import trackr.logic.commands.exceptions.CommandException; +import trackr.logic.parser.TrackrParser; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.Model; +import trackr.model.ReadOnlyMenu; +import trackr.model.ReadOnlyOrderList; +import trackr.model.ReadOnlySupplierList; +import trackr.model.ReadOnlyTaskList; +import trackr.model.menu.ItemProfit; +import trackr.model.menu.ItemSellingPrice; +import trackr.model.menu.MenuItem; +import trackr.model.order.Order; +import trackr.model.person.Supplier; +import trackr.model.task.Task; +import trackr.storage.Storage; + +/** + * The main LogicManager of the app. + */ +public class LogicManager implements Logic { + public static final String FILE_OPS_ERROR_MESSAGE = "Could not save data to file: "; + private final Logger logger = LogsCenter.getLogger(LogicManager.class); + + private final Model model; + private final Storage storage; + private final TrackrParser trackrParser; + + /** + * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. + */ + public LogicManager(Model model, Storage storage) { + this.model = model; + this.storage = storage; + trackrParser = new TrackrParser(); + } + + /** + * Parses the given command and executes it. + * + * @param commandText The command as entered by the user. + * @return Feedback message of the operation for display. + * @throws CommandException If command given cannot be executed. + * @throws ParseException If command string given cannot be parsed. + */ + @Override + public CommandResult execute(String commandText) throws CommandException, ParseException { + logger.info("----------------[USER COMMAND][" + commandText + "]"); + + CommandResult commandResult; + Command command = trackrParser.parseCommand(commandText); + commandResult = command.execute(model); + try { + storage.saveTrackr(model.getSupplierList(), model.getTaskList(), model.getMenu(), model.getOrderList()); + } catch (IOException ioe) { + throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); + } + + return commandResult; + } + + @Override + public ReadOnlySupplierList getSupplierList() { + return model.getSupplierList(); + } + + @Override + public ObservableList getFilteredSupplierList() { + return model.getFilteredSupplierList(); + } + + //@@author liumc-sg-reused + @Override + public ReadOnlyTaskList getTaskList() { + return model.getTaskList(); + } + + @Override + public ObservableList getFilteredTaskList() { + return model.getFilteredTaskList(); + } + //@@author + + @Override + public ReadOnlyMenu getMenu() { + return model.getMenu(); + } + + @Override + public ObservableList getFilteredMenu() { + return model.getFilteredMenu(); + } + + //@@author chongweiguan-reused + @Override + public ReadOnlyOrderList getOrderList() { + return model.getOrderList(); + } + + @Override + public ObservableList getFilteredOrderList() { + return model.getFilteredOrderList(); + } + //@@author + + //@@author arkarsg + @Override + public ItemProfit getTotalProfits() { + return model.getTotalProfits(); + } + //@@author + + //@author arkarsg + @Override + public ItemSellingPrice getTotalSales() { + return model.getTotalSales(); + } + //@@author + + //@@author liumc-sg-reused + @Override + public Path getTrackrFilePath() { + return model.getTrackrFilePath(); + } + //@@author + + @Override + public GuiSettings getGuiSettings() { + return model.getGuiSettings(); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + model.setGuiSettings(guiSettings); + } +} diff --git a/src/main/java/trackr/logic/commands/AddItemCommand.java b/src/main/java/trackr/logic/commands/AddItemCommand.java new file mode 100644 index 00000000000..0b77880952c --- /dev/null +++ b/src/main/java/trackr/logic/commands/AddItemCommand.java @@ -0,0 +1,66 @@ +package trackr.logic.commands; + +import static java.util.Objects.requireNonNull; +import static trackr.commons.util.CollectionUtil.requireAllNonNull; + +import trackr.logic.commands.exceptions.CommandException; +import trackr.model.Model; +import trackr.model.ModelEnum; +import trackr.model.item.Item; + +/** + * Adds an Item to the item list. + */ +//@@author liumc-sg-reused +public abstract class AddItemCommand extends Command { + public static final String MESSAGE_SUCCESS = "New %s added: %s"; + public static final String MESSAGE_DUPLICATE_ITEM = "This %s already exists in the %s list"; + + private final ModelEnum modelEnum; + private final T toAdd; + + /** + * Creates an AddItemCommand to add the specified {@code Item} to its respective item list. + * + * @param item The item to be added. + * @param modelEnum A representation of the name of the list we add to. + */ + public AddItemCommand(T item, ModelEnum modelEnum) { + requireAllNonNull(item, modelEnum); + toAdd = item; + this.modelEnum = modelEnum; + } + //@@author + + public T getItemToAdd() { + return toAdd; + } + + /** + * Adds the item {@code toAdd} to its respective item list. + * + * @param model {@code Model} which the command should operate on. + * @return Success message of the add operation for display. + * @throws CommandException If item to be added is considered to be duplicates + * with any of the other existing items in the list. + */ + //@@author liumc-sg-reused + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasItem(toAdd, modelEnum)) { + throw new CommandException(String.format(MESSAGE_DUPLICATE_ITEM, modelEnum, modelEnum)); + } + + model.addItem(toAdd, modelEnum); + return new CommandResult(String.format(MESSAGE_SUCCESS, modelEnum, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddItemCommand // instanceof handles nulls + && toAdd.equals(((AddItemCommand) other).toAdd)); + } +} diff --git a/src/main/java/trackr/logic/commands/ClearItemCommand.java b/src/main/java/trackr/logic/commands/ClearItemCommand.java new file mode 100644 index 00000000000..fe6d3c24c53 --- /dev/null +++ b/src/main/java/trackr/logic/commands/ClearItemCommand.java @@ -0,0 +1,41 @@ +package trackr.logic.commands; + +import static java.util.Objects.requireNonNull; + +import trackr.model.Model; +import trackr.model.ModelEnum; +import trackr.model.item.Item; + +/** + * Clears the item list. + */ +//@@author liumc-sg-reused +public abstract class ClearItemCommand extends Command { + + public static final String MESSAGE_SUCCESS = "%s list has been cleared!"; + + private final ModelEnum modelEnum; + + /** + * Creates an ClearItemCommand to clear the specified item list. + * + * @param modelEnum A representation of the name of the list to clear. + */ + public ClearItemCommand(ModelEnum modelEnum) { + requireNonNull(modelEnum); + this.modelEnum = modelEnum; + } + + /** + * Clears the specified item list. + * + * @param model {@code Model} which the command should operate on. + * @return Success message of the clear operation for display. + */ + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.setItemList(modelEnum); + return new CommandResult(String.format(MESSAGE_SUCCESS, modelEnum)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/trackr/logic/commands/Command.java similarity index 61% rename from src/main/java/seedu/address/logic/commands/Command.java rename to src/main/java/trackr/logic/commands/Command.java index 64f18992160..f0cbc93f074 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/trackr/logic/commands/Command.java @@ -1,7 +1,8 @@ -package seedu.address.logic.commands; +package trackr.logic.commands; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; +import trackr.logic.commands.exceptions.CommandException; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.Model; /** * Represents a command with hidden internal logic and the ability to be executed. @@ -12,9 +13,9 @@ 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; + public abstract CommandResult execute(Model model) throws CommandException, ParseException; } diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/trackr/logic/commands/CommandResult.java similarity index 97% rename from src/main/java/seedu/address/logic/commands/CommandResult.java rename to src/main/java/trackr/logic/commands/CommandResult.java index 92f900b7916..0bdba05acee 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/trackr/logic/commands/CommandResult.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package trackr.logic.commands; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/trackr/logic/commands/DeleteItemCommand.java b/src/main/java/trackr/logic/commands/DeleteItemCommand.java new file mode 100644 index 00000000000..b7919131620 --- /dev/null +++ b/src/main/java/trackr/logic/commands/DeleteItemCommand.java @@ -0,0 +1,60 @@ +package trackr.logic.commands; + +import static java.util.Objects.requireNonNull; +import static trackr.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.List; + +import trackr.commons.core.Messages; +import trackr.commons.core.index.Index; +import trackr.logic.commands.exceptions.CommandException; +import trackr.model.Model; +import trackr.model.ModelEnum; +import trackr.model.item.Item; + +/** + * Deletes an item identified using it's displayed index from the item list. + */ +//@@author liumc-sg-reused +public abstract class DeleteItemCommand extends Command { + + public static final String MESSAGE_DELETE_ITEM_SUCCESS = "Deleted %s: %s"; + + private final Index targetIndex; + private ModelEnum modelEnum; + + /** + * Creates an DeleteItemCommand to delete the specified {@code Item} at the target index. + * + * @param targetIndex The index of the target item to be deleted. + * @param modelEnum A representation of the name of the list where item is deleted. + */ + //@@author liumc-sg-reused + public DeleteItemCommand(Index targetIndex, ModelEnum modelEnum) { + requireAllNonNull(targetIndex, modelEnum); + this.targetIndex = targetIndex; + this.modelEnum = modelEnum; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredItemList(modelEnum); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(String.format(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX, + modelEnum.toString().toLowerCase())); + } + + Item itemToDelete = lastShownList.get(targetIndex.getZeroBased()); + model.deleteItem(itemToDelete, modelEnum); + return new CommandResult(String.format(MESSAGE_DELETE_ITEM_SUCCESS, modelEnum, itemToDelete)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteItemCommand // instanceof handles nulls + && targetIndex.equals(((DeleteItemCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/trackr/logic/commands/EditItemCommand.java b/src/main/java/trackr/logic/commands/EditItemCommand.java new file mode 100644 index 00000000000..602385cfe30 --- /dev/null +++ b/src/main/java/trackr/logic/commands/EditItemCommand.java @@ -0,0 +1,132 @@ +package trackr.logic.commands; + +import static java.util.Objects.requireNonNull; +import static trackr.commons.util.CollectionUtil.requireAllNonNull; +import static trackr.model.Model.PREDICATE_SHOW_ALL_ITEMS; + +import java.util.List; +import java.util.Optional; + +import trackr.commons.core.Messages; +import trackr.commons.core.index.Index; +import trackr.logic.commands.exceptions.CommandException; +import trackr.model.Model; +import trackr.model.ModelEnum; +import trackr.model.item.Item; +import trackr.model.item.ItemDescriptor; +import trackr.model.menu.MenuItem; +import trackr.model.order.OrderDescriptor; + +/** + * Edits the details of an existing item in the item list. + */ +public abstract class EditItemCommand extends Command { + + public static final String MESSAGE_EDIT_ITEM_SUCCESS = "Edited %s: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_ITEM = "This %1$s already exists in the %1$s list."; + public static final String MESSAGE_NO_MENU_ITEM = "No such item in your menu."; + + private final ModelEnum modelEnum; + private final Index index; + private final ItemDescriptor editItemDescriptor; + + /** + * Creates an EditItemCommand to edit the specified {@code Item} at the target index with the new details. + * + * @param index Index of the item to be edited in the filtered list. + * @param editItemDescriptor Details to edit the item with. + * @param modelEnum A representation of the name of the list to edit. + */ + public EditItemCommand(Index index, ItemDescriptor editItemDescriptor, ModelEnum modelEnum) { + requireAllNonNull(index, editItemDescriptor, modelEnum); + this.modelEnum = modelEnum; + this.index = index; + this.editItemDescriptor = editItemDescriptor; + } + + /** + * Edits the item at the target index. + * + * @param model {@code Model} which the command should operate on. + * @return Success message of the edit operation result for display. + * @throws CommandException If given target index is out of bounds, + * if edited item is considered to be duplicates + * with any of the existing items in the list, + * or (when editing an order) if an order's edited order item field + * does not match with an existing menu item. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownItemList = model.getFilteredItemList(modelEnum); + + if (index.getZeroBased() >= lastShownItemList.size()) { + throw new CommandException(String.format(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX, + modelEnum.toString().toLowerCase())); + } + + Item itemToEdit = lastShownItemList.get(index.getZeroBased()); + Item editedItem; + + if (editItemDescriptor instanceof OrderDescriptor) { + OrderDescriptor editOrderDescriptor = (OrderDescriptor) editItemDescriptor; + if (editOrderDescriptor.getOrderName().isPresent()) { + List currentMenuItems = model.getFilteredMenu(); + Optional existingMenuItem = + currentMenuItems.stream() + .filter(menuItem -> menuItem.getItemName().getName() + .equals(editOrderDescriptor.getOrderName().get().getName())) + .findAny(); + + if (existingMenuItem.isEmpty()) { + throw new CommandException(MESSAGE_NO_MENU_ITEM); + } + //there should only be one menu item that matches if there is any + editOrderDescriptor.setOrderItem(existingMenuItem.get()); + } + editedItem = createEditedItem((T) itemToEdit, (ItemDescriptor) editOrderDescriptor); + } else { + editedItem = createEditedItem((T) itemToEdit, editItemDescriptor); + } + + if (!itemToEdit.isSameItem(editedItem) && model.hasItem(editedItem, modelEnum)) { + throw new CommandException(String.format(MESSAGE_DUPLICATE_ITEM, modelEnum.toString().toLowerCase())); + } + + model.setItem(itemToEdit, editedItem, modelEnum); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS, modelEnum); + return new CommandResult(String.format(MESSAGE_EDIT_ITEM_SUCCESS, modelEnum.toString().toLowerCase(), + editedItem)); + } + + /** + * Creates and returns a {@code Item} with the details of {@code itemToEdit} + * edited with {@code editItemDescriptor}. + * + * @param itemToEdit The item to be edited. + * @param itemDescriptor Details to edit the item with. + * @return An edited {@code Item}. + */ + //@@author liumc-sg-reused + protected abstract T createEditedItem(T itemToEdit, ItemDescriptor itemDescriptor); + + //@@author liumc-sg-reused + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditItemCommand)) { + return false; + } + + // state check + EditItemCommand e = (EditItemCommand) other; + return index.equals(e.index) + && editItemDescriptor.equals(e.editItemDescriptor); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/trackr/logic/commands/ExitCommand.java similarity index 54% rename from src/main/java/seedu/address/logic/commands/ExitCommand.java rename to src/main/java/trackr/logic/commands/ExitCommand.java index 3dd85a8ba90..d4db4ab05b6 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/trackr/logic/commands/ExitCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package trackr.logic.commands; -import seedu.address.model.Model; +import trackr.model.Model; /** * Terminates the program. @@ -9,8 +9,14 @@ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; - public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; + public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Trackr as requested ..."; + /** + * Returns the exit acknowledgement message. + * + * @param model {@code Model} which the command should operate on. + * @return Exit acknowledgement message for display. + */ @Override public CommandResult execute(Model model) { return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); diff --git a/src/main/java/trackr/logic/commands/FindItemCommand.java b/src/main/java/trackr/logic/commands/FindItemCommand.java new file mode 100644 index 00000000000..6fc1800e012 --- /dev/null +++ b/src/main/java/trackr/logic/commands/FindItemCommand.java @@ -0,0 +1,55 @@ +package trackr.logic.commands; + +import static java.util.Objects.requireNonNull; +import static trackr.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.function.Predicate; + +import trackr.commons.core.Messages; +import trackr.model.Model; +import trackr.model.ModelEnum; +import trackr.model.item.Item; + +/** + * Finds and lists all items in item list. + */ +//@@author liumc-sg-reused +public abstract class FindItemCommand extends Command { + + private final ModelEnum modelEnum; + private final Predicate predicate; + + /** + * Creates an FindItemCommand to find all {@code Item} that match the predicate. + * + * @param predicate The predicate to find the items with. + * @param modelEnum A representation of the name of the list to find the items in. + */ + public FindItemCommand(Predicate predicate, ModelEnum modelEnum) { + requireAllNonNull(predicate, modelEnum); + this.predicate = predicate; + this.modelEnum = modelEnum; + } + + /** + * Finds the items that matches the given predicate and updates the filtered item list with the items found. + * + * @param model {@code Model} which the command should operate on. + * @return Success message of the find operation for display. + */ + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredItemList(predicate, modelEnum); + return new CommandResult(String.format(Messages.MESSAGE_ITEMS_LISTED_OVERVIEW, + model.getFilteredItemList(modelEnum).size(), + modelEnum.toString().toLowerCase())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindItemCommand // instanceof handles nulls + && predicate.equals(((FindItemCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/trackr/logic/commands/HelpCommand.java similarity index 69% rename from src/main/java/seedu/address/logic/commands/HelpCommand.java rename to src/main/java/trackr/logic/commands/HelpCommand.java index bf824f91bd0..6da7a7b6b33 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/trackr/logic/commands/HelpCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package trackr.logic.commands; -import seedu.address.model.Model; +import trackr.model.Model; /** * Format full help instructions for every command for display. @@ -14,6 +14,12 @@ public class HelpCommand extends Command { public static final String SHOWING_HELP_MESSAGE = "Opened help window."; + /** + * Returns the show help message. + * + * @param model {@code Model} which the command should operate on. + * @return Show help message for display. + */ @Override public CommandResult execute(Model model) { return new CommandResult(SHOWING_HELP_MESSAGE, true, false); diff --git a/src/main/java/trackr/logic/commands/ListItemCommand.java b/src/main/java/trackr/logic/commands/ListItemCommand.java new file mode 100644 index 00000000000..6cb9d1bc36e --- /dev/null +++ b/src/main/java/trackr/logic/commands/ListItemCommand.java @@ -0,0 +1,42 @@ +package trackr.logic.commands; + +import static java.util.Objects.requireNonNull; +import static trackr.model.Model.PREDICATE_SHOW_ALL_ITEMS; + +import trackr.model.Model; +import trackr.model.ModelEnum; +import trackr.model.item.Item; + +/** + * Lists all items in the item list to the user. + */ +//@@author liumc-sg-reused +public abstract class ListItemCommand extends Command { + + public static final String MESSAGE_SUCCESS = "Listed all %ss"; + + private final ModelEnum modelEnum; + + /** + * Creates an ListItemCommand to list all the items. + * + * @param modelEnum A representation of the name of the list to list out. + */ + public ListItemCommand(ModelEnum modelEnum) { + requireNonNull(modelEnum); + this.modelEnum = modelEnum; + } + + /** + * Updates the filtered item list with all the existing items. + * + * @param model {@code Model} which the command should operate on. + * @return Success message of the list operation for display. + */ + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS, modelEnum); + return new CommandResult(String.format(MESSAGE_SUCCESS, modelEnum.toString().toLowerCase())); + } +} diff --git a/src/main/java/trackr/logic/commands/TabCommand.java b/src/main/java/trackr/logic/commands/TabCommand.java new file mode 100644 index 00000000000..491dc498fa1 --- /dev/null +++ b/src/main/java/trackr/logic/commands/TabCommand.java @@ -0,0 +1,45 @@ +package trackr.logic.commands; + +import static java.util.Objects.requireNonNull; +import static trackr.logic.parser.CliSyntax.PREFIX_TAB; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.exceptions.CommandException; +import trackr.model.Model; +import trackr.model.ObservableTabIndex; + +//@@uthor arkarsg +/** + * Switches to a tab specified by the user + */ +public class TabCommand extends Command { + public static final String MESSAGE_SUCCESS = "Switched tab."; + public static final String COMMAND_WORD = "tab"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Switches to specified tab. " + + "Parameters: " + + PREFIX_TAB + "TAB NAME\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_TAB + "HOME"; + + private final Index targetTab; + + /** + * Creates a SwitchTabCommand to switch to specified tab {@code index} + */ + public TabCommand(Index targetTab) { + requireNonNull(targetTab); + this.targetTab = targetTab; + } + + /** + * Switches to the specified tab. + * + * @param unused {@code Model} which is not used. + * @return Success message of the tab operation for display. + */ + @Override + public CommandResult execute(Model unused) throws CommandException { + ObservableTabIndex.updateToTab(targetTab.getZeroBased()); + return new CommandResult(String.format(MESSAGE_SUCCESS)); + } +} diff --git a/src/main/java/trackr/logic/commands/UploadCsvCommand.java b/src/main/java/trackr/logic/commands/UploadCsvCommand.java new file mode 100644 index 00000000000..5e87d8f1d03 --- /dev/null +++ b/src/main/java/trackr/logic/commands/UploadCsvCommand.java @@ -0,0 +1,72 @@ +package trackr.logic.commands; +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import trackr.logic.commands.exceptions.CommandException; +import trackr.logic.parser.TrackrParser; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.Model; + +//@@author chongweiguan-reused +/** + * Uploads information retrieved from a csv file onto Trackr. + */ +public class UploadCsvCommand extends Command { + + public static final String COMMAND_WORD = "upload_csv"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Allows you to upload " + + "a csv file and convert it to orders, tasks and or contacts."; + + public static final String MESSAGE_SUCCESS = "Csv file successfully uploaded"; + private List commands; + + /** + * Creates an UploadCSVCommand to upload data onto Trackr. + * + * @commands A list that contains commands to be executed for uploading of information onto Trackr. + */ + public UploadCsvCommand(List commands) { + requireNonNull(commands); + this.commands = commands; + } + + /** + * Parses and executes all the commands in the list {@code commands} + * to add data retrieved from csv file into Trackr's data file. + * + * @param model {@code Model} which the command should operate on. + * @return Success message of the upload csv operation for display. + * @throws CommandException If any of the commands cannot be executed successfully. + * @throws ParseException If any of the commands in the list {@code commands} cannot be parsed + * or executed properly. + */ + @Override + public CommandResult execute(Model model) throws CommandException, ParseException { + TrackrParser trackrParser = new TrackrParser(); + for (int i = 0; i < commands.size(); i++) { + try { + Command command = trackrParser.parseCommand(commands.get(i)); + command.execute(model); + + } catch (CommandException e) { + // do nothing, continue adding other items from csv file + noAction(); + } + } + return new CommandResult(String.format(MESSAGE_SUCCESS)); + } + + @Override + public boolean equals(Object other) { + return other == this + || other instanceof UploadCsvCommand + && commands.equals(((UploadCsvCommand) other).commands); + } + + private void noAction() { + return; + } + //@@author +} diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/trackr/logic/commands/exceptions/CommandException.java similarity index 84% rename from src/main/java/seedu/address/logic/commands/exceptions/CommandException.java rename to src/main/java/trackr/logic/commands/exceptions/CommandException.java index a16bd14f2cd..17854b2d9cc 100644 --- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java +++ b/src/main/java/trackr/logic/commands/exceptions/CommandException.java @@ -1,4 +1,6 @@ -package seedu.address.logic.commands.exceptions; +package trackr.logic.commands.exceptions; + +import trackr.logic.commands.Command; /** * Represents an error which occurs during execution of a {@link Command}. diff --git a/src/main/java/trackr/logic/commands/menu/AddMenuItemCommand.java b/src/main/java/trackr/logic/commands/menu/AddMenuItemCommand.java new file mode 100644 index 00000000000..77d0692634b --- /dev/null +++ b/src/main/java/trackr/logic/commands/menu/AddMenuItemCommand.java @@ -0,0 +1,35 @@ +package trackr.logic.commands.menu; + +import static trackr.logic.parser.CliSyntax.PREFIX_COST; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_PRICE; + +import trackr.logic.commands.AddItemCommand; +import trackr.model.ModelEnum; +import trackr.model.menu.MenuItem; + +/** + * Adds a menuItem to the Menu. + */ +//@@author changgittyhub-reused +public class AddMenuItemCommand extends AddItemCommand { + public static final String COMMAND_WORD = "add_item"; + public static final String COMMAND_WORD_SHORTCUT = "add_i"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an item to the menu. " + + "Parameters: " + + PREFIX_NAME + "ITEM NAME " + + PREFIX_PRICE + "PRICE " + + PREFIX_COST + "COST \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "Chocolate Cookies " + + PREFIX_PRICE + "2 " + + PREFIX_COST + "0.50 "; + + /** + * Creates an AddMenuItemCommand to add the specified {@code MenuItem}. + */ + public AddMenuItemCommand(MenuItem menuItem) { + super(menuItem, ModelEnum.MENUITEM); + } +} diff --git a/src/main/java/trackr/logic/commands/menu/ClearMenuItemCommand.java b/src/main/java/trackr/logic/commands/menu/ClearMenuItemCommand.java new file mode 100644 index 00000000000..26ee2c9b4eb --- /dev/null +++ b/src/main/java/trackr/logic/commands/menu/ClearMenuItemCommand.java @@ -0,0 +1,22 @@ +package trackr.logic.commands.menu; + +import trackr.logic.commands.ClearItemCommand; +import trackr.model.ModelEnum; +import trackr.model.menu.MenuItem; + +/** + * Clears the menu. + */ +//@@author changgittyhub-reused +public class ClearMenuItemCommand extends ClearItemCommand { + + public static final String COMMAND_WORD = "clear_menu"; + public static final String COMMAND_WORD_SHORTCUT = "clear_m"; + + /** + * Creates a ClearMenuItemCommand to clear the menu. + */ + public ClearMenuItemCommand() { + super(ModelEnum.MENUITEM); + } +} diff --git a/src/main/java/trackr/logic/commands/menu/DeleteMenuItemCommand.java b/src/main/java/trackr/logic/commands/menu/DeleteMenuItemCommand.java new file mode 100644 index 00000000000..20bbf888295 --- /dev/null +++ b/src/main/java/trackr/logic/commands/menu/DeleteMenuItemCommand.java @@ -0,0 +1,30 @@ +package trackr.logic.commands.menu; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.DeleteItemCommand; +import trackr.model.ModelEnum; +import trackr.model.menu.MenuItem; + +/** + * Deletes the menu item identified using it's displayed index from the filtered menu. + */ +//@@author changgittyhub-reused +public class DeleteMenuItemCommand extends DeleteItemCommand { + + public static final String COMMAND_WORD = "delete_item"; + public static final String COMMAND_WORD_SHORTCUT = "delete_i"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the menu item identified by the index number used in the displayed menu.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + /** + * Creates a DeleteMenuItemCommand to delete the menu item at the given index. + * + * @param targetIndex The index of the menu item to be deleted. + */ + public DeleteMenuItemCommand(Index targetIndex) { + super(targetIndex, ModelEnum.MENUITEM); + } +} diff --git a/src/main/java/trackr/logic/commands/menu/EditMenuItemCommand.java b/src/main/java/trackr/logic/commands/menu/EditMenuItemCommand.java new file mode 100644 index 00000000000..035db81042a --- /dev/null +++ b/src/main/java/trackr/logic/commands/menu/EditMenuItemCommand.java @@ -0,0 +1,64 @@ +package trackr.logic.commands.menu; + +import static trackr.logic.parser.CliSyntax.PREFIX_COST; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_PRICE; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.EditItemCommand; +import trackr.model.ModelEnum; +import trackr.model.item.ItemDescriptor; +import trackr.model.menu.ItemCost; +import trackr.model.menu.ItemName; +import trackr.model.menu.ItemSellingPrice; +import trackr.model.menu.MenuItem; +import trackr.model.menu.MenuItemDescriptor; + +/** + * Edits the details of an existing item in the menu. + */ +//@@author changgittyhub-reused +public class EditMenuItemCommand extends EditItemCommand { + + public static final String COMMAND_WORD = "edit_item"; + public static final String COMMAND_WORD_SHORTCUT = "edit_i"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Edits the details of the item identified " + + "by the index number used in the displayed task list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_NAME + "ITEM NAME] " + + "[" + PREFIX_PRICE + "ITEM PRICE] " + + "[" + PREFIX_COST + "ITEM COST]\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_NAME + "Cupcake " + + PREFIX_PRICE + "2" + + PREFIX_COST + "1"; + + /** + * Creates an EditMenuItemCommand to edit the menu item at the given index. + * + * @param index The index of the menu item to be edited. + * @param editMenuItemDescriptor The details to edit the menu item with. + */ + public EditMenuItemCommand(Index index, MenuItemDescriptor editMenuItemDescriptor) { + super(index, new MenuItemDescriptor(editMenuItemDescriptor), ModelEnum.MENUITEM); + } + + @Override + protected MenuItem createEditedItem(MenuItem itemToEdit, ItemDescriptor itemDescriptor) { + assert itemToEdit != null; + + MenuItemDescriptor menuItemDescriptor = (MenuItemDescriptor) itemDescriptor; + + ItemName updatedItemName = + menuItemDescriptor.getItemName().orElse(itemToEdit.getItemName()); + ItemSellingPrice updatedItemPrice = + menuItemDescriptor.getItemPrice().orElse(itemToEdit.getItemPrice()); + ItemCost updatedItemCost = + menuItemDescriptor.getItemCost().orElse(itemToEdit.getItemCost()); + + return new MenuItem(updatedItemName, updatedItemPrice, updatedItemCost); + } +} diff --git a/src/main/java/trackr/logic/commands/menu/FindMenuItemCommand.java b/src/main/java/trackr/logic/commands/menu/FindMenuItemCommand.java new file mode 100644 index 00000000000..78e9f1da61c --- /dev/null +++ b/src/main/java/trackr/logic/commands/menu/FindMenuItemCommand.java @@ -0,0 +1,32 @@ +package trackr.logic.commands.menu; + +import trackr.logic.commands.FindItemCommand; +import trackr.model.ModelEnum; +import trackr.model.menu.ItemNameContainsKeywordsPredicate; +import trackr.model.menu.MenuItem; + +/** + * Finds and lists all item in menu whose description contains any of the argument keywords. + * Keyword matching is case-insensitive. + */ +//@@author changgittyhub-reused +public class FindMenuItemCommand extends FindItemCommand { + + public static final String COMMAND_WORD = "find_item"; + public static final String COMMAND_WORD_SHORTCUT = "find_i"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Finds all menu items whose description 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 + " chocolate cookies"; + + /** + * Creates an FindMenuItemCommand to find all {@code MenuItem} that match the predicate. + * + * @param predicate The predicate to find the menu items with. + */ + public FindMenuItemCommand(ItemNameContainsKeywordsPredicate predicate) { + super(predicate, ModelEnum.MENUITEM); + } +} diff --git a/src/main/java/trackr/logic/commands/menu/ListMenuItemCommand.java b/src/main/java/trackr/logic/commands/menu/ListMenuItemCommand.java new file mode 100644 index 00000000000..3f94cf7b31c --- /dev/null +++ b/src/main/java/trackr/logic/commands/menu/ListMenuItemCommand.java @@ -0,0 +1,22 @@ +package trackr.logic.commands.menu; + +import trackr.logic.commands.ListItemCommand; +import trackr.model.ModelEnum; +import trackr.model.menu.MenuItem; + +/** + * Lists all items in the menu to the user. + */ +//@@author changgittyhub-reused +public class ListMenuItemCommand extends ListItemCommand { + + public static final String COMMAND_WORD = "list_menu"; + public static final String COMMAND_WORD_SHORTCUT = "list_m"; + + /** + * Creates a ListMenuItemCommand to list all the menu items. + */ + public ListMenuItemCommand() { + super(ModelEnum.MENUITEM); + } +} diff --git a/src/main/java/trackr/logic/commands/order/AddOrderCommand.java b/src/main/java/trackr/logic/commands/order/AddOrderCommand.java new file mode 100644 index 00000000000..88705a3c968 --- /dev/null +++ b/src/main/java/trackr/logic/commands/order/AddOrderCommand.java @@ -0,0 +1,94 @@ +package trackr.logic.commands.order; + +import static java.util.Objects.requireNonNull; +import static trackr.commons.core.Messages.MESSAGE_DUPLICATE_ITEM; +import static trackr.commons.core.Messages.MESSAGE_NO_MENU_ITEM; +import static trackr.commons.core.Messages.MESSAGE_SUCCESS; +import static trackr.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static trackr.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_ORDERNAME; +import static trackr.logic.parser.CliSyntax.PREFIX_ORDERQUANTITY; +import static trackr.logic.parser.CliSyntax.PREFIX_PHONE; +import static trackr.logic.parser.CliSyntax.PREFIX_STATUS; + +import java.util.List; + +import trackr.logic.commands.Command; +import trackr.logic.commands.CommandResult; +import trackr.logic.commands.exceptions.CommandException; +import trackr.model.Model; +import trackr.model.ModelEnum; +import trackr.model.menu.MenuItem; +import trackr.model.order.Order; + +//@@author chongweiguan-reused +/** + * Adds an order to the order list. + */ +public class AddOrderCommand extends Command { + public static final String COMMAND_WORD = "add_order"; + public static final String COMMAND_WORD_SHORTCUT = "add_o"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a order to the order list. " + + "Parameters: " + + PREFIX_ORDERNAME + "ORDER NAME " + + PREFIX_ORDERQUANTITY + "ORDER QUANTITY " + + PREFIX_DEADLINE + "DEADLINE " + + "[" + PREFIX_STATUS + "STATUS] " + + PREFIX_NAME + "CUSTOMER'S NAME " + + PREFIX_PHONE + "CUSTOMER'S PHONE NUMBER " + + PREFIX_ADDRESS + "CUSTOMER'S ADDRESS\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_ORDERNAME + "Chocolate Cookies " + + PREFIX_ORDERQUANTITY + "5 " + + PREFIX_DEADLINE + "01/01/2024 " + + PREFIX_STATUS + "N " + + PREFIX_NAME + "John Doe " + + PREFIX_PHONE + "12345678 " + + PREFIX_ADDRESS + "123 Smith Street "; + + private final ModelEnum modelEnum; + private final Order toAdd; + + /** + * Creates an AddOrderCommand to add the specified {@code Order}. + * + * @param order The order to be added. + */ + public AddOrderCommand(Order order) { + requireNonNull(order); + this.modelEnum = ModelEnum.ORDER; + this.toAdd = order; + } + + //@@author arkarsg + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List currentMenuItems = model.getFilteredMenu(); + MenuItem existingItem = currentMenuItems.stream() + .filter(item -> item.getItemName().getName() + .equals(toAdd.getOrderName().getName())) + .findAny() + .orElseThrow(() -> + new CommandException(MESSAGE_NO_MENU_ITEM)); + Order validOrder = new Order(existingItem, toAdd.getOrderDeadline(), toAdd.getOrderStatus(), + toAdd.getOrderQuantity(), toAdd.getCustomer()); + if (model.hasItem(validOrder, modelEnum)) { + throw new CommandException(String.format(MESSAGE_DUPLICATE_ITEM, modelEnum, modelEnum)); + } + + model.addItem(validOrder, modelEnum); + return new CommandResult(String.format(MESSAGE_SUCCESS, modelEnum, validOrder)); + } + //@@author + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof AddOrderCommand + && toAdd.equals(((AddOrderCommand) other).toAdd)); + } + //@@author +} diff --git a/src/main/java/trackr/logic/commands/order/ClearOrderCommand.java b/src/main/java/trackr/logic/commands/order/ClearOrderCommand.java new file mode 100644 index 00000000000..45f86562042 --- /dev/null +++ b/src/main/java/trackr/logic/commands/order/ClearOrderCommand.java @@ -0,0 +1,21 @@ +package trackr.logic.commands.order; + +import trackr.logic.commands.ClearItemCommand; +import trackr.model.ModelEnum; +import trackr.model.order.Order; + +/** + * Clears the order list. + */ +public class ClearOrderCommand extends ClearItemCommand { + + public static final String COMMAND_WORD = "clear_order"; + public static final String COMMAND_WORD_SHORTCUT = "clear_o"; + + /** + * Creates a ClearOrderCommand to clear the order list. + */ + public ClearOrderCommand() { + super(ModelEnum.ORDER); + } +} diff --git a/src/main/java/trackr/logic/commands/order/DeleteOrderCommand.java b/src/main/java/trackr/logic/commands/order/DeleteOrderCommand.java new file mode 100644 index 00000000000..053d760c794 --- /dev/null +++ b/src/main/java/trackr/logic/commands/order/DeleteOrderCommand.java @@ -0,0 +1,30 @@ +package trackr.logic.commands.order; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.DeleteItemCommand; +import trackr.model.ModelEnum; +import trackr.model.order.Order; + +//@@author chongweiguan-reused +/** + * Deletes an order identified using it's displayed index from the order list. + */ +public class DeleteOrderCommand extends DeleteItemCommand { + public static final String COMMAND_WORD = "delete_order"; + public static final String COMMAND_WORD_SHORTCUT = "delete_o"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the order identified by the index number used in the displayed order list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + /** + * Creates a DeleteOrderCommand to delete the order at given index. + * + * @param targetIndex The index of the order to be deleted. + */ + public DeleteOrderCommand(Index targetIndex) { + super(targetIndex, ModelEnum.ORDER); + } + //@@author +} diff --git a/src/main/java/trackr/logic/commands/order/EditOrderCommand.java b/src/main/java/trackr/logic/commands/order/EditOrderCommand.java new file mode 100644 index 00000000000..8ce374c6fff --- /dev/null +++ b/src/main/java/trackr/logic/commands/order/EditOrderCommand.java @@ -0,0 +1,89 @@ +package trackr.logic.commands.order; + +import static trackr.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static trackr.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_ORDERNAME; +import static trackr.logic.parser.CliSyntax.PREFIX_ORDERQUANTITY; +import static trackr.logic.parser.CliSyntax.PREFIX_PHONE; +import static trackr.logic.parser.CliSyntax.PREFIX_STATUS; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.EditItemCommand; +import trackr.model.ModelEnum; +import trackr.model.item.ItemDescriptor; +import trackr.model.menu.MenuItem; +import trackr.model.order.Order; +import trackr.model.order.OrderDeadline; +import trackr.model.order.OrderDescriptor; +import trackr.model.order.OrderQuantity; +import trackr.model.order.OrderStatus; +import trackr.model.person.Customer; +import trackr.model.person.PersonAddress; +import trackr.model.person.PersonName; +import trackr.model.person.PersonPhone; + +/** + * Edits the details of an existing order in the order list. + */ +//@@author changgittyhub-reused +public class EditOrderCommand extends EditItemCommand { + public static final String COMMAND_WORD = "edit_order"; + public static final String COMMAND_WORD_SHORTCUT = "edit_o"; + + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_ITEM = "This %s already exists in the %s list."; + + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the order identified " + + "by the index number used in the displayed order list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_ORDERNAME + "ORDER NAME] " + + "[" + PREFIX_DEADLINE + "ORDER DEADLINE] " + + "[" + PREFIX_STATUS + "ORDER STATUS] " + + "[" + PREFIX_ORDERQUANTITY + "ORDER QUANTITY] " + + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_PHONE + "PHONE] " + + "[" + PREFIX_ADDRESS + "ADDRESS] " + + "Example: " + COMMAND_WORD + " 2 " + + PREFIX_NAME + "Birthday Cake" + + PREFIX_STATUS + "D"; + + /** + * Creates an EditOrderCommand to edit the order at the given index. + * + * @param index The index of the order to be edited. + * @param editOrderDescriptor The details to edit the order with. + */ + //@@author changgittyhub-reused + public EditOrderCommand(Index index, OrderDescriptor editOrderDescriptor) { + super(index, new OrderDescriptor(editOrderDescriptor), ModelEnum.ORDER); + } + + //@@author changgittyhub-reused + @Override + protected Order createEditedItem(Order itemToEdit, ItemDescriptor itemDescriptor) { + assert itemToEdit != null; + + OrderDescriptor orderDescriptor = (OrderDescriptor) itemDescriptor; + MenuItem updatedOrderItem = + orderDescriptor.getOrderItem().orElse(itemToEdit.getOrderItem()); + OrderDeadline updatedOrderDeadline = + orderDescriptor.getOrderDeadline().orElse(itemToEdit.getOrderDeadline()); + OrderStatus updatedOrderStatus = + orderDescriptor.getOrderStatus().orElse(itemToEdit.getOrderStatus()); + OrderQuantity updatedOrderQuantity = + orderDescriptor.getOrderQuantity().orElse(itemToEdit.getOrderQuantity()); + PersonName updatedCustomerName = + orderDescriptor.getCustomerName().orElse(itemToEdit.getCustomer().getCustomerName()); + PersonPhone updatedCustomerPhone = + orderDescriptor.getCustomerPhone().orElse(itemToEdit.getCustomer().getCustomerPhone()); + PersonAddress updatedCustomerAddress = + orderDescriptor.getCustomerAddress().orElse(itemToEdit.getCustomer().getCustomerAddress()); + Customer updatedCustomer = new Customer(updatedCustomerName, updatedCustomerPhone, updatedCustomerAddress); + + return new Order(updatedOrderItem, updatedOrderDeadline, updatedOrderStatus, + updatedOrderQuantity, updatedCustomer); + } +} diff --git a/src/main/java/trackr/logic/commands/order/FindOrderCommand.java b/src/main/java/trackr/logic/commands/order/FindOrderCommand.java new file mode 100644 index 00000000000..4ca01533e7d --- /dev/null +++ b/src/main/java/trackr/logic/commands/order/FindOrderCommand.java @@ -0,0 +1,32 @@ +package trackr.logic.commands.order; + +import static trackr.logic.parser.CliSyntax.PREFIX_ORDERNAME; + +import trackr.logic.commands.FindItemCommand; +import trackr.model.ModelEnum; +import trackr.model.order.Order; +import trackr.model.order.OrderContainsKeywordsPredicate; + +/** + * Finds and lists all order in order list whose description contains any of the argument keywords. + */ +//@@author changgittyhub-reused +public class FindOrderCommand extends FindItemCommand { + public static final String COMMAND_WORD = "find_order"; + public static final String COMMAND_WORD_SHORTCUT = "find_o"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Finds all orders whose description 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 + " " + PREFIX_ORDERNAME + "Chocolate cookie"; + + /** + * Creates an FindOrderCommand to find all {@code Order} that match the predicate. + * + * @param predicate The predicate to find the orders with. + */ + public FindOrderCommand(OrderContainsKeywordsPredicate predicate) { + super(predicate, ModelEnum.ORDER); + } +} diff --git a/src/main/java/trackr/logic/commands/order/ListOrderCommand.java b/src/main/java/trackr/logic/commands/order/ListOrderCommand.java new file mode 100644 index 00000000000..cb8fc3fdb6c --- /dev/null +++ b/src/main/java/trackr/logic/commands/order/ListOrderCommand.java @@ -0,0 +1,21 @@ +package trackr.logic.commands.order; + +import trackr.logic.commands.ListItemCommand; +import trackr.model.ModelEnum; +import trackr.model.order.Order; + +/** + * Lists all orders in the order list to the user. + */ +public class ListOrderCommand extends ListItemCommand { + + public static final String COMMAND_WORD = "list_order"; + public static final String COMMAND_WORD_SHORTCUT = "list_o"; + + /** + * Creates a ListOrderCommand to list all the orders. + */ + public ListOrderCommand() { + super(ModelEnum.ORDER); + } +} diff --git a/src/main/java/trackr/logic/commands/order/SortOrdersCommand.java b/src/main/java/trackr/logic/commands/order/SortOrdersCommand.java new file mode 100644 index 00000000000..b2aed522cf5 --- /dev/null +++ b/src/main/java/trackr/logic/commands/order/SortOrdersCommand.java @@ -0,0 +1,58 @@ +package trackr.logic.commands.order; + +import static java.util.Objects.requireNonNull; +import static trackr.logic.parser.CliSyntax.PREFIX_CRITERIA; + +import trackr.logic.commands.Command; +import trackr.logic.commands.CommandResult; +import trackr.model.Model; +import trackr.model.order.SortOrdersComparator; + +/** + * Sorts all orders in the order list using a criteria given. + */ +//Solution below adapted from AB3 +public class SortOrdersCommand extends Command { + public static final String COMMAND_WORD = "sort_order"; + public static final String COMMAND_WORD_SHORTCUT = "sort_o"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Sorts all orders " + + "based on given criteria.\n" + + "Parameters: " + "[" + PREFIX_CRITERIA + "CRITERIA] " + + "Example: " + COMMAND_WORD + PREFIX_CRITERIA + "Status"; + + public static final String MESSAGE_SUCCESS = "Orders sorted!"; + + private SortOrdersComparator sortOrdersComparator; + + /** + * Creates a SortOrdersCommand to sort the order list. + * + * @param sortOrdersComparator The comparator used to compare 2 orders according to a given criteria. + */ + public SortOrdersCommand(SortOrdersComparator sortOrdersComparator) { + requireNonNull(sortOrdersComparator); + this.sortOrdersComparator = sortOrdersComparator; + } + + /** + * Sorts the filtered order list using the sortOrdersComparator. + * + * @param model {@code Model} which the command should operate on. + * @return Success message of the sort operation for display. + */ + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.sortFilteredOrderList(sortOrdersComparator); + return new CommandResult(String.format(MESSAGE_SUCCESS)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SortOrdersCommand // instanceof handles nulls + && sortOrdersComparator.equals(((SortOrdersCommand) other).sortOrdersComparator)); // state check + } + +} diff --git a/src/main/java/trackr/logic/commands/supplier/AddSupplierCommand.java b/src/main/java/trackr/logic/commands/supplier/AddSupplierCommand.java new file mode 100644 index 00000000000..a5b6ab3fc9f --- /dev/null +++ b/src/main/java/trackr/logic/commands/supplier/AddSupplierCommand.java @@ -0,0 +1,45 @@ +package trackr.logic.commands.supplier; + +import static trackr.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static trackr.logic.parser.CliSyntax.PREFIX_EMAIL; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_PHONE; +import static trackr.logic.parser.CliSyntax.PREFIX_TAG; + +import trackr.logic.commands.AddItemCommand; +import trackr.model.ModelEnum; +import trackr.model.person.Supplier; + +/** + * Adds a supplier to the supplier list. + */ +//@@author liumc-sg-reused +public class AddSupplierCommand extends AddItemCommand { + + public static final String COMMAND_WORD = "add_supplier"; + public static final String COMMAND_WORD_SHORTCUT = "add_s"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a supplier to Trackr. " + + "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 + "wheat " + + PREFIX_TAG + "eggs"; + + /** + * Creates an AddSupplierCommand to add the specified {@code Supplier}. + * + * @param supplier The supplier to be added. + */ + public AddSupplierCommand(Supplier supplier) { + super(supplier, ModelEnum.SUPPLIER); + } +} diff --git a/src/main/java/trackr/logic/commands/supplier/ClearSupplierCommand.java b/src/main/java/trackr/logic/commands/supplier/ClearSupplierCommand.java new file mode 100644 index 00000000000..0f14646b98a --- /dev/null +++ b/src/main/java/trackr/logic/commands/supplier/ClearSupplierCommand.java @@ -0,0 +1,21 @@ +package trackr.logic.commands.supplier; + +import trackr.logic.commands.ClearItemCommand; +import trackr.model.ModelEnum; +import trackr.model.person.Supplier; + +/** + * Clears the supplier list. + */ +public class ClearSupplierCommand extends ClearItemCommand { + + public static final String COMMAND_WORD = "clear_supplier"; + public static final String COMMAND_WORD_SHORTCUT = "clear_s"; + + /** + * Creates a ClearSupplierCommand to clear the supplier list. + */ + public ClearSupplierCommand() { + super(ModelEnum.SUPPLIER); + } +} diff --git a/src/main/java/trackr/logic/commands/supplier/DeleteSupplierCommand.java b/src/main/java/trackr/logic/commands/supplier/DeleteSupplierCommand.java new file mode 100644 index 00000000000..d169c5cee3c --- /dev/null +++ b/src/main/java/trackr/logic/commands/supplier/DeleteSupplierCommand.java @@ -0,0 +1,29 @@ +package trackr.logic.commands.supplier; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.DeleteItemCommand; +import trackr.model.ModelEnum; +import trackr.model.person.Supplier; + +/** + * Deletes a supplier identified using it's displayed index from the supplier list. + */ +public class DeleteSupplierCommand extends DeleteItemCommand { + + public static final String COMMAND_WORD = "delete_supplier"; + public static final String COMMAND_WORD_SHORTCUT = "delete_s"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the supplier identified by the index number used in the displayed supplier list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + /** + * Creates a DeleteSupplierCommand to delete the supplier at the given index. + * + * @param targetIndex The index of the supplier to be deleted. + */ + public DeleteSupplierCommand(Index targetIndex) { + super(targetIndex, ModelEnum.SUPPLIER); + } +} diff --git a/src/main/java/trackr/logic/commands/supplier/EditSupplierCommand.java b/src/main/java/trackr/logic/commands/supplier/EditSupplierCommand.java new file mode 100644 index 00000000000..dc826f0be10 --- /dev/null +++ b/src/main/java/trackr/logic/commands/supplier/EditSupplierCommand.java @@ -0,0 +1,68 @@ +package trackr.logic.commands.supplier; + +import static trackr.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static trackr.logic.parser.CliSyntax.PREFIX_EMAIL; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_PHONE; +import static trackr.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Set; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.EditItemCommand; +import trackr.model.ModelEnum; +import trackr.model.commons.Tag; +import trackr.model.item.ItemDescriptor; +import trackr.model.person.PersonAddress; +import trackr.model.person.PersonDescriptor; +import trackr.model.person.PersonEmail; +import trackr.model.person.PersonName; +import trackr.model.person.PersonPhone; +import trackr.model.person.Supplier; + +/** + * Edits the details of an existing supplier in the supplier list. + */ +//@@author liumc-sg-reused +public class EditSupplierCommand extends EditItemCommand { + + public static final String COMMAND_WORD = "edit_supplier"; + public static final String COMMAND_WORD_SHORTCUT = "edit_s"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the supplier identified " + + "by the index number used in the displayed supplier 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"; + + /** + * Creates an EditSupplierCommand to edit the supplier at given index. + * @param index of the supplier in the filtered supplier list to edit + * @param supplierDescriptor details to edit the supplier with + */ + public EditSupplierCommand(Index index, PersonDescriptor supplierDescriptor) { + super(index, new PersonDescriptor(supplierDescriptor), ModelEnum.SUPPLIER); + } + + @Override + protected Supplier createEditedItem(Supplier itemToEdit, ItemDescriptor itemDescriptor) { + assert itemToEdit != null; + + PersonDescriptor supplierDescriptor = (PersonDescriptor) itemDescriptor; + + PersonName updatedName = supplierDescriptor.getName().orElse(itemToEdit.getPersonName()); + PersonPhone updatedPersonPhone = supplierDescriptor.getPhone().orElse(itemToEdit.getPersonPhone()); + PersonEmail updatedPersonEmail = supplierDescriptor.getEmail().orElse(itemToEdit.getPersonEmail()); + PersonAddress updatedPersonAddress = supplierDescriptor.getAddress().orElse(itemToEdit.getPersonAddress()); + Set updatedTags = supplierDescriptor.getTags().orElse(itemToEdit.getPersonTags()); + + return new Supplier(updatedName, updatedPersonPhone, updatedPersonEmail, updatedPersonAddress, updatedTags); + } +} diff --git a/src/main/java/trackr/logic/commands/supplier/FindSupplierCommand.java b/src/main/java/trackr/logic/commands/supplier/FindSupplierCommand.java new file mode 100644 index 00000000000..e7b9613ef22 --- /dev/null +++ b/src/main/java/trackr/logic/commands/supplier/FindSupplierCommand.java @@ -0,0 +1,30 @@ +package trackr.logic.commands.supplier; + +import trackr.logic.commands.FindItemCommand; +import trackr.model.ModelEnum; +import trackr.model.person.PersonNameContainsKeywordsPredicate; +import trackr.model.person.Supplier; + +/** + * Finds and lists all suppliers in address book whose name contains any of the argument keywords. + * Keyword matching is case-insensitive. + */ +public class FindSupplierCommand extends FindItemCommand { + + public static final String COMMAND_WORD = "find_supplier"; + public static final String COMMAND_WORD_SHORTCUT = "find_s"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all supplier 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"; + + /** + * Creates an FindSupplierCommand to find all {@code Supplier} that match the predicate. + * + * @param predicate The predicate to find the suppliers with. + */ + public FindSupplierCommand(PersonNameContainsKeywordsPredicate predicate) { + super(predicate, ModelEnum.SUPPLIER); + } +} diff --git a/src/main/java/trackr/logic/commands/supplier/ListSupplierCommand.java b/src/main/java/trackr/logic/commands/supplier/ListSupplierCommand.java new file mode 100644 index 00000000000..f2f05046e4b --- /dev/null +++ b/src/main/java/trackr/logic/commands/supplier/ListSupplierCommand.java @@ -0,0 +1,21 @@ +package trackr.logic.commands.supplier; + +import trackr.logic.commands.ListItemCommand; +import trackr.model.ModelEnum; +import trackr.model.person.Supplier; + +/** + * Lists all suppliers in the supplier list to the user. + */ +public class ListSupplierCommand extends ListItemCommand { + + public static final String COMMAND_WORD = "list_supplier"; + public static final String COMMAND_WORD_SHORTCUT = "list_s"; + + /** + * Creates a ListSupplierCommand to list all the suppliers. + */ + public ListSupplierCommand() { + super(ModelEnum.SUPPLIER); + } +} diff --git a/src/main/java/trackr/logic/commands/task/AddTaskCommand.java b/src/main/java/trackr/logic/commands/task/AddTaskCommand.java new file mode 100644 index 00000000000..7b7e150ca7c --- /dev/null +++ b/src/main/java/trackr/logic/commands/task/AddTaskCommand.java @@ -0,0 +1,38 @@ +package trackr.logic.commands.task; + +import static trackr.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_STATUS; + +import trackr.logic.commands.AddItemCommand; +import trackr.model.ModelEnum; +import trackr.model.task.Task; +/** + * Adds a task to the task list. + */ +public class AddTaskCommand extends AddItemCommand { + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + public static final String COMMAND_WORD = "add_task"; + public static final String COMMAND_WORD_SHORTCUT = "add_t"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a task to the task list. " + + "Parameters: " + + PREFIX_NAME + "TASK NAME " + + PREFIX_DEADLINE + "DEADLINE " + + "[" + PREFIX_STATUS + "STATUS]...\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "Sort Inventory " + + PREFIX_DEADLINE + "01/01/2024 " + + PREFIX_STATUS + "N "; + + /** + * Creates an AddTaskCommand to add the specified {@code Task}. + * + * @param task The task to be added. + */ + public AddTaskCommand(Task task) { + super(task, ModelEnum.TASK); + } + //@@author +} diff --git a/src/main/java/trackr/logic/commands/task/ClearTaskCommand.java b/src/main/java/trackr/logic/commands/task/ClearTaskCommand.java new file mode 100644 index 00000000000..97df4da899b --- /dev/null +++ b/src/main/java/trackr/logic/commands/task/ClearTaskCommand.java @@ -0,0 +1,21 @@ +package trackr.logic.commands.task; + +import trackr.logic.commands.ClearItemCommand; +import trackr.model.ModelEnum; +import trackr.model.task.Task; + +/** + * Clears the task list. + */ +public class ClearTaskCommand extends ClearItemCommand { + + public static final String COMMAND_WORD = "clear_task"; + public static final String COMMAND_WORD_SHORTCUT = "clear_t"; + + /** + * Creates a ClearTaskCommand to clear the task list. + */ + public ClearTaskCommand() { + super(ModelEnum.TASK); + } +} diff --git a/src/main/java/trackr/logic/commands/task/DeleteTaskCommand.java b/src/main/java/trackr/logic/commands/task/DeleteTaskCommand.java new file mode 100644 index 00000000000..33cbd4c0e93 --- /dev/null +++ b/src/main/java/trackr/logic/commands/task/DeleteTaskCommand.java @@ -0,0 +1,30 @@ +package trackr.logic.commands.task; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.DeleteItemCommand; +import trackr.model.ModelEnum; +import trackr.model.task.Task; + +/** + * Deletes a task identified using it's displayed index from the task list. + */ +//@@author liumc-sg-reused +public class DeleteTaskCommand extends DeleteItemCommand { + + public static final String COMMAND_WORD = "delete_task"; + public static final String COMMAND_WORD_SHORTCUT = "delete_t"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the task identified by the index number used in the displayed task list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + /** + * Creates a DeleteTaskCommand to delete the task at the given index. + * + * @param targetIndex The index of the task to be deleted. + */ + public DeleteTaskCommand(Index targetIndex) { + super(targetIndex, ModelEnum.TASK); + } +} diff --git a/src/main/java/trackr/logic/commands/task/EditTaskCommand.java b/src/main/java/trackr/logic/commands/task/EditTaskCommand.java new file mode 100644 index 00000000000..ce15fe2dd29 --- /dev/null +++ b/src/main/java/trackr/logic/commands/task/EditTaskCommand.java @@ -0,0 +1,62 @@ +package trackr.logic.commands.task; + +import static trackr.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_STATUS; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.EditItemCommand; +import trackr.model.ModelEnum; +import trackr.model.item.ItemDescriptor; +import trackr.model.task.Task; +import trackr.model.task.TaskDeadline; +import trackr.model.task.TaskDescriptor; +import trackr.model.task.TaskName; +import trackr.model.task.TaskStatus; + +/** + * Edits the details of an existing task in the task list. + */ +public class EditTaskCommand extends EditItemCommand { + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + public static final String COMMAND_WORD = "edit_task"; + public static final String COMMAND_WORD_SHORTCUT = "edit_t"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the task identified " + + "by the index number used in the displayed task list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_NAME + "TASK NAME] " + + "[" + PREFIX_DEADLINE + "TASK DEADLINE] " + + "[" + PREFIX_STATUS + "TASK STATUS]\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_NAME + "Buy sugar " + + PREFIX_STATUS + "D"; + + /** + * Creates an EditTaskCommand to edit the task at given index. + * @param index of the task in the filtered task list to edit. + * @param editTaskDescriptor details to edit the task with. + */ + public EditTaskCommand(Index index, TaskDescriptor editTaskDescriptor) { + super(index, new TaskDescriptor(editTaskDescriptor), ModelEnum.TASK); + } + + @Override + protected Task createEditedItem(Task itemToEdit, ItemDescriptor itemDescriptor) { + assert itemToEdit != null; + + TaskDescriptor taskDescriptor = (TaskDescriptor) itemDescriptor; + + TaskName updatedTaskName = + taskDescriptor.getTaskName().orElse(itemToEdit.getTaskName()); + TaskDeadline updatedTaskDeadline = + taskDescriptor.getTaskDeadline().orElse(itemToEdit.getTaskDeadline()); + TaskStatus updatedTaskStatus = + taskDescriptor.getTaskStatus().orElse(itemToEdit.getTaskStatus()); + + return new Task(updatedTaskName, updatedTaskDeadline, updatedTaskStatus); + } + //@@author +} diff --git a/src/main/java/trackr/logic/commands/task/FindTaskCommand.java b/src/main/java/trackr/logic/commands/task/FindTaskCommand.java new file mode 100644 index 00000000000..7c82afc7a30 --- /dev/null +++ b/src/main/java/trackr/logic/commands/task/FindTaskCommand.java @@ -0,0 +1,31 @@ +package trackr.logic.commands.task; + +import trackr.logic.commands.FindItemCommand; +import trackr.model.ModelEnum; +import trackr.model.task.Task; +import trackr.model.task.TaskContainsKeywordsPredicate; + +/** + * Finds and lists all task in task list whose description contains any of the argument keywords. + * Keyword matching is case-insensitive. + */ +//@@author liumc-sg-reused +public class FindTaskCommand extends FindItemCommand { + + public static final String COMMAND_WORD = "find_task"; + public static final String COMMAND_WORD_SHORTCUT = "find_t"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all tasks whose description 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 + " flour buy"; + + /** + * Creates an FindTaskCommand to find all {@code Task} that match the predicate. + * + * @param predicate The predicate to find the tasks with. + */ + public FindTaskCommand(TaskContainsKeywordsPredicate predicate) { + super(predicate, ModelEnum.TASK); + } +} diff --git a/src/main/java/trackr/logic/commands/task/ListTaskCommand.java b/src/main/java/trackr/logic/commands/task/ListTaskCommand.java new file mode 100644 index 00000000000..8288b57f5e8 --- /dev/null +++ b/src/main/java/trackr/logic/commands/task/ListTaskCommand.java @@ -0,0 +1,24 @@ +package trackr.logic.commands.task; + +import trackr.logic.commands.ListItemCommand; +import trackr.model.ModelEnum; +import trackr.model.task.Task; + +/** + * Lists all tasks in the task list to the user. + */ +public class ListTaskCommand extends ListItemCommand { + + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + public static final String COMMAND_WORD = "list_task"; + public static final String COMMAND_WORD_SHORTCUT = "list_t"; + + /** + * Creates a ListTaskCommand to list all the tasks. + */ + public ListTaskCommand() { + super(ModelEnum.TASK); + } + //@@author +} diff --git a/src/main/java/trackr/logic/commands/task/SortTasksCommand.java b/src/main/java/trackr/logic/commands/task/SortTasksCommand.java new file mode 100644 index 00000000000..e95bfdd5ea0 --- /dev/null +++ b/src/main/java/trackr/logic/commands/task/SortTasksCommand.java @@ -0,0 +1,56 @@ +package trackr.logic.commands.task; + +import static java.util.Objects.requireNonNull; +import static trackr.logic.parser.CliSyntax.PREFIX_CRITERIA; + +import trackr.logic.commands.Command; +import trackr.logic.commands.CommandResult; +import trackr.model.Model; +import trackr.model.task.SortTasksComparator; + +/** + * Sorts all task in task list using a criteria given. + */ +//Solution below adapted from AB3 +public class SortTasksCommand extends Command { + public static final String COMMAND_WORD = "sort_task"; + public static final String COMMAND_WORD_SHORTCUT = "sort_t"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Sorts all tasks " + + "based on given criteria.\n" + + "Parameters: " + "[" + PREFIX_CRITERIA + "CRITERIA] " + + "Example: " + COMMAND_WORD + PREFIX_CRITERIA + "Deadline"; + + public static final String MESSAGE_SUCCESS = "Tasks sorted!"; + + private SortTasksComparator sortTasksComparator; + + /** + * Creates a SortTasksCommand to sort the task list. + * + * @param sortTasksComparator The comparator used to compare 2 tasks according to a given criteria. + */ + public SortTasksCommand(SortTasksComparator sortTasksComparator) { + requireNonNull(sortTasksComparator); + this.sortTasksComparator = sortTasksComparator; + } + + /** + * Sorts the filtered task list using the sortTasksComparator. + * + * @param model {@code Model} which the command should operate on. + * @return Success message of the sort operation for display. + */ + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.sortFilteredTaskList(sortTasksComparator); + return new CommandResult(String.format(MESSAGE_SUCCESS)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SortTasksCommand); // instanceof handles nulls + } +} diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/trackr/logic/parser/ArgumentMultimap.java similarity index 81% rename from src/main/java/seedu/address/logic/parser/ArgumentMultimap.java rename to src/main/java/trackr/logic/parser/ArgumentMultimap.java index 954c8e18f8e..b15da1a6864 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ b/src/main/java/trackr/logic/parser/ArgumentMultimap.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package trackr.logic.parser; import java.util.ArrayList; import java.util.HashMap; @@ -15,16 +15,22 @@ */ public class ArgumentMultimap { - /** Prefixes mapped to their respective arguments**/ + /** Prefixes mapped to their respective arguments **/ private final Map> argMultimap = new HashMap<>(); + //@@author HmuuMyatMoe-reused + //Reused from + //`https://github.com/nus-cs2103-AY2223S2/tp/blob/master/src/main/java/seedu/address/logic/parser + // /ArgumentMultimap.java` /** * 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. + * 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. */ + //@@author public void put(Prefix prefix, String argValue) { List argValues = getAllValues(prefix); argValues.add(argValue); diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/trackr/logic/parser/ArgumentTokenizer.java similarity index 99% rename from src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java rename to src/main/java/trackr/logic/parser/ArgumentTokenizer.java index 5c9aebfa488..b50aedf44f4 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ b/src/main/java/trackr/logic/parser/ArgumentTokenizer.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package trackr.logic.parser; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/main/java/trackr/logic/parser/CliSyntax.java b/src/main/java/trackr/logic/parser/CliSyntax.java new file mode 100644 index 00000000000..f9bce7269fa --- /dev/null +++ b/src/main/java/trackr/logic/parser/CliSyntax.java @@ -0,0 +1,26 @@ +package trackr.logic.parser; + +/** + * 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/"); + + public static final Prefix PREFIX_DEADLINE = new Prefix("d/"); + public static final Prefix PREFIX_STATUS = new Prefix("s/"); + public static final Prefix PREFIX_ORDERNAME = new Prefix("on/"); + public static final Prefix PREFIX_ORDERQUANTITY = new Prefix("q/"); + + public static final Prefix PREFIX_PRICE = new Prefix("pr/"); + public static final Prefix PREFIX_COST = new Prefix("c/"); + + public static final Prefix PREFIX_TAB = new Prefix("t/"); + + public static final Prefix PREFIX_CRITERIA = new Prefix("c/"); +} diff --git a/src/main/java/trackr/logic/parser/CriteriaEnum.java b/src/main/java/trackr/logic/parser/CriteriaEnum.java new file mode 100644 index 00000000000..5cff4bcf193 --- /dev/null +++ b/src/main/java/trackr/logic/parser/CriteriaEnum.java @@ -0,0 +1,24 @@ +package trackr.logic.parser; + +/** + * Enum representing sorting criteria. + */ +public enum CriteriaEnum { + TIME_ADDED("Time added"), + DEADLINE("Deadline"), + STATUS("Status"), + NAME("Name"), + STATUS_AND_DEADLINE("Status and Deadline"); + + private final String strRepresentation; + + CriteriaEnum(String strRepresentation) { + this.strRepresentation = strRepresentation; + } + + @Override + public String toString() { + return strRepresentation; + } + +} diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/trackr/logic/parser/Parser.java similarity index 72% rename from src/main/java/seedu/address/logic/parser/Parser.java rename to src/main/java/trackr/logic/parser/Parser.java index d6551ad8e3f..4bfb049de03 100644 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ b/src/main/java/trackr/logic/parser/Parser.java @@ -1,7 +1,7 @@ -package seedu.address.logic.parser; +package trackr.logic.parser; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import trackr.logic.commands.Command; +import trackr.logic.parser.exceptions.ParseException; /** * Represents a Parser that is able to parse user input into a {@code Command} of type {@code T}. @@ -10,6 +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 */ T parse(String userInput) throws ParseException; diff --git a/src/main/java/trackr/logic/parser/ParserUtil.java b/src/main/java/trackr/logic/parser/ParserUtil.java new file mode 100644 index 00000000000..8af998a883c --- /dev/null +++ b/src/main/java/trackr/logic/parser/ParserUtil.java @@ -0,0 +1,403 @@ +package trackr.logic.parser; + +import static java.util.Objects.requireNonNull; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import trackr.commons.core.index.Index; +import trackr.commons.util.StringUtil; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.TabEnum; +import trackr.model.commons.Name; +import trackr.model.commons.Tag; +import trackr.model.menu.ItemCost; +import trackr.model.menu.ItemName; +import trackr.model.menu.ItemSellingPrice; +import trackr.model.order.OrderDeadline; +import trackr.model.order.OrderName; +import trackr.model.order.OrderQuantity; +import trackr.model.order.OrderStatus; +import trackr.model.person.PersonAddress; +import trackr.model.person.PersonEmail; +import trackr.model.person.PersonName; +import trackr.model.person.PersonPhone; +import trackr.model.task.TaskDeadline; +import trackr.model.task.TaskName; +import trackr.model.task.TaskStatus; + +/** + * 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_TAB = "No such tab."; + public static final String MESSAGE_INVALID_CRITERIA = "Criteria given must be one of the types: " + + "`Time_added`, `Deadline`, `Status`, `Name`, `Status_and_Deadline` or blank"; + /** + * 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 { + String trimmedIndex = oneBasedIndex.trim(); + if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { + throw new ParseException(MESSAGE_INVALID_INDEX); + } + return Index.fromOneBased(Integer.parseInt(trimmedIndex)); + } + + /** + * Parses a {@code String name} into a {@code Name}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code name} is invalid. + */ + //@@author liumc-sg-reused + public static PersonName parseName(String name) throws ParseException { + requireNonNull(name); + String trimmedName = name.trim(); + if (!Name.isValidName(trimmedName)) { + throw new ParseException(PersonName.MESSAGE_CONSTRAINTS); + } + return new PersonName(trimmedName); + } + + /** + * Parses a {@code String phone} into a {@code Phone}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code phone} is invalid. + */ + public static PersonPhone parsePhone(String phone) throws ParseException { + requireNonNull(phone); + String trimmedPhone = phone.trim(); + if (!PersonPhone.isValidPersonPhone(trimmedPhone)) { + throw new ParseException(PersonPhone.MESSAGE_CONSTRAINTS); + } + return new PersonPhone(trimmedPhone); + } + + /** + * Parses a {@code String address} into an {@code Address}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code address} is invalid. + */ + public static PersonAddress parseAddress(String address) throws ParseException { + requireNonNull(address); + String trimmedAddress = address.trim(); + if (!PersonAddress.isValidPersonAddress(trimmedAddress)) { + throw new ParseException(PersonAddress.MESSAGE_CONSTRAINTS); + } + return new PersonAddress(trimmedAddress); + } + + /** + * Parses a {@code String email} into an {@code Email}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code email} is invalid. + */ + public static PersonEmail parseEmail(String email) throws ParseException { + requireNonNull(email); + String trimmedEmail = email.trim(); + if (!PersonEmail.isValidPersonEmail(trimmedEmail)) { + throw new ParseException(PersonEmail.MESSAGE_CONSTRAINTS); + } + return new PersonEmail(trimmedEmail); + } + //@@author + + /** + * Parses a {@code String tag} into a {@code Tag}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code tag} is invalid. + */ + public static Tag parseTag(String tag) throws ParseException { + requireNonNull(tag); + String trimmedTag = tag.trim(); + if (!Tag.isValidTagName(trimmedTag)) { + throw new ParseException(Tag.MESSAGE_CONSTRAINTS); + } + return new Tag(trimmedTag); + } + + /** + * Parses {@code Collection tags} into a {@code Set}. + */ + public static Set parseTags(Collection tags) throws ParseException { + requireNonNull(tags); + final Set tagSet = new HashSet<>(); + for (String tagName : tags) { + tagSet.add(parseTag(tagName)); + } + return tagSet; + } + + //Solution below adapted from AB3. + /** + * Parses a {@code Optional criteria} into a {@code CriteriaEnum}. + * + * @param criteria The {@code Optional} representation of sorting criteria. + * @throws ParseException If given {@code criteria} is empty + * or cannot be parsed into a valid CriteriaEnum. + */ + public static CriteriaEnum parseSortingCriteria(Optional criteria) throws ParseException { + requireNonNull(criteria); + if (!criteria.isPresent()) { + return CriteriaEnum.STATUS_AND_DEADLINE; + } + + String trimmedCriteria = criteria.get().trim().toUpperCase(); + try { + return CriteriaEnum.valueOf(trimmedCriteria); + } catch (IllegalArgumentException e) { + throw new ParseException(MESSAGE_INVALID_CRITERIA); + } + } + + //========================Parse those related to task================================== + + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + /** + * Parses a {@code String taskName} into a {@code TaskName}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code taskName} is invalid. + */ + public static TaskName parseTaskName(String taskName) throws ParseException { + requireNonNull(taskName); + String trimmedTaskName = taskName.trim(); + if (!TaskName.isValidName(trimmedTaskName)) { + throw new ParseException(TaskName.MESSAGE_CONSTRAINTS); + } + return new TaskName(trimmedTaskName); + } + + /** + * Parses a {@code String taskDeadline} into a {@code TaskDeadline}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code taskDeadline} is invalid. + */ + public static TaskDeadline parseTaskDeadline(String taskDeadline) throws ParseException { + requireNonNull(taskDeadline); + String trimmedTaskDeadline = taskDeadline.trim(); + if (!TaskDeadline.isValidDeadline(trimmedTaskDeadline)) { + throw new ParseException(TaskDeadline.MESSAGE_CONSTRAINTS); + } + return new TaskDeadline(trimmedTaskDeadline); + } + //@@author + + //Solution below adapted from AB3 with some reuse. + /** + * Parses a {@code String taskStatus} into a {@code TaskStatus}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code taskStatus} is invalid. + */ + public static TaskStatus parseTaskStatus(Optional taskStatus) throws ParseException { + requireNonNull(taskStatus); + if (!taskStatus.isPresent()) { + return new TaskStatus(); + } + + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + String trimmedTaskStatus = taskStatus.get().trim(); + if (!TaskStatus.isValidStatus(trimmedTaskStatus, TaskStatus.STATUSES)) { + throw new ParseException(TaskStatus.MESSAGE_CONSTRAINTS); + } + return new TaskStatus(trimmedTaskStatus); + //@@author + } + //========================Parse those related to menu item================================== + + /** + * Parses a {@code String itemName} into a {@code ItemName}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code itemName} is invalid. + */ + public static ItemName parseItemName(String itemName) throws ParseException { + requireNonNull(itemName); + String trimmedItemName = itemName.trim(); + if (!ItemName.isValidName(trimmedItemName)) { + throw new ParseException(ItemName.MESSAGE_CONSTRAINTS); + } + return new ItemName(trimmedItemName); + } + + /** + * Parses a {@code String itemPrice} into a {@code ItemPrice}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code itemPrice} is invalid. + */ + public static ItemSellingPrice parseItemPrice(String itemPrice) throws ParseException { + requireNonNull(itemPrice); + String trimmedItemPrice = itemPrice.trim(); + if (!ItemSellingPrice.isValidPrice(trimmedItemPrice)) { + throw new ParseException(ItemSellingPrice.MESSAGE_CONSTRAINTS); + } + return new ItemSellingPrice(trimmedItemPrice); + } + + /** + * Parses a {@code String itemCost} into a {@code ItemCost}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code itemCost} is invalid. + */ + public static ItemCost parseItemCost(String itemCost) throws ParseException { + requireNonNull(itemCost); + String trimmedItemCost = itemCost.trim(); + if (!ItemCost.isValidPrice(trimmedItemCost)) { + throw new ParseException(ItemCost.MESSAGE_CONSTRAINTS); + } + return new ItemCost(trimmedItemCost); + } + + //========================Parse those related to order================================== + + //@@author chongweiguan-reused + /** + * Parses a {@code String orderName} into a {@code OrderName}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code orderName} is invalid. + */ + public static OrderName parseOrderName(String orderName) throws ParseException { + requireNonNull(orderName); + String trimmedTaskName = orderName.trim(); + if (!OrderName.isValidName(trimmedTaskName)) { + throw new ParseException(OrderName.MESSAGE_CONSTRAINTS); + } + return new OrderName(trimmedTaskName); + } + + /** + * Parses a {@code String OrderDeadline} into a {@code OrderDeadline}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code orderDeadline} is invalid. + */ + public static OrderDeadline parseOrderDeadline(String orderDeadline) throws ParseException { + requireNonNull(orderDeadline); + String trimmedOrderDeadline = orderDeadline.trim(); + if (!OrderDeadline.isValidDeadline(trimmedOrderDeadline)) { + throw new ParseException(OrderDeadline.MESSAGE_CONSTRAINTS); + } + return new OrderDeadline(trimmedOrderDeadline); + } + + /** + * Parses a {@code Optional OrderStatus} into a {@code OrderStatus}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code orderStatus} is invalid. + */ + public static OrderStatus parseOrderStatus(Optional orderStatus) throws ParseException { + requireNonNull(orderStatus); + if (!orderStatus.isPresent()) { + return new OrderStatus(); + } + + String trimmedOrderStatus = orderStatus.get().trim(); + if (!OrderStatus.isValidStatus(trimmedOrderStatus, OrderStatus.STATUSES)) { + throw new ParseException(OrderStatus.MESSAGE_CONSTRAINTS); + } + return new OrderStatus(trimmedOrderStatus); + } + + /** + * Parses a {@code String OrderQuantity} into a {@code OrderQuantity}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code orderQuantity} is invalid. + */ + public static OrderQuantity parseOrderQuantity(String orderQuantity) throws ParseException { + requireNonNull(orderQuantity); + String trimmedOrderQuantity = orderQuantity.trim(); + if (!OrderQuantity.isValidQuantity(trimmedOrderQuantity)) { + throw new ParseException(OrderQuantity.MESSAGE_CONSTRAINTS); + } + return new OrderQuantity(trimmedOrderQuantity); + } + //@@author + + /** + * Parses a {@code String CustomerName} into a {@code CustomerName}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code customerName} is invalid. + */ + //@@author liumc-sg-reused + public static PersonName parseCustomerName(String customerName) throws ParseException { + requireNonNull(customerName); + String trimmedCustomerName = customerName.trim(); + if (!PersonName.isValidName(trimmedCustomerName)) { + throw new ParseException(PersonName.MESSAGE_CONSTRAINTS); + } + return new PersonName(trimmedCustomerName); + } + //@@author + + /** + * Parses a {@code String CustomerPhone} into a {@code CustomerPhone}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code customerPhone} is invalid. + */ + public static PersonPhone parseCustomerPhone(String customerPhone) throws ParseException { + requireNonNull(customerPhone); + String trimmedCustomerPhone = customerPhone.trim(); + if (!PersonPhone.isValidPersonPhone(trimmedCustomerPhone)) { + throw new ParseException(PersonPhone.MESSAGE_CONSTRAINTS); + } + return new PersonPhone(trimmedCustomerPhone); + } + + /** + * Parses a {@code String CustomerAddress} into a {@code CustomerAddress}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code customerAddress} is invalid. + */ + public static PersonAddress parseCustomerAddress(String customerAddress) throws ParseException { + requireNonNull(customerAddress); + String trimmedCustomerAddress = customerAddress.trim(); + if (!PersonAddress.isValidPersonAddress(trimmedCustomerAddress)) { + throw new ParseException(PersonAddress.MESSAGE_CONSTRAINTS); + } + return new PersonAddress(trimmedCustomerAddress); + } + + //========================Parse those related to tab================================== + //@author arkarsg + /** + * Parses {@code targetTabName} into an {@code Index} and returns it. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the specified {@code targetTabName} is invalid (not a recognised Tab). + */ + public static Index parseTab(String targetTabName) throws ParseException { + String trimmedTab = targetTabName.trim(); + int tabIndex; + try { + tabIndex = TabEnum.getTabIndex(trimmedTab); + } catch (IllegalArgumentException e) { + throw new ParseException(MESSAGE_INVALID_TAB); + } + return Index.fromZeroBased(tabIndex); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/trackr/logic/parser/Prefix.java similarity index 95% rename from src/main/java/seedu/address/logic/parser/Prefix.java rename to src/main/java/trackr/logic/parser/Prefix.java index c859d5fa5db..b9d9ee81fb9 100644 --- a/src/main/java/seedu/address/logic/parser/Prefix.java +++ b/src/main/java/trackr/logic/parser/Prefix.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package trackr.logic.parser; /** * A prefix that marks the beginning of an argument in an arguments string. diff --git a/src/main/java/trackr/logic/parser/TabCommandParser.java b/src/main/java/trackr/logic/parser/TabCommandParser.java new file mode 100644 index 00000000000..d181e3e3d48 --- /dev/null +++ b/src/main/java/trackr/logic/parser/TabCommandParser.java @@ -0,0 +1,40 @@ +package trackr.logic.parser; + +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static trackr.logic.parser.CliSyntax.PREFIX_TAB; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.TabCommand; +import trackr.logic.parser.exceptions.ParseException; + +//@@author arkarsg-reused +/** + * Parses input arguments and creates a new TabCommand object. + */ +public class TabCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of TabCommand + * and returns a TabCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public TabCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_TAB); + + if (!isPrefixPresent(argMultimap, PREFIX_TAB) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, TabCommand.MESSAGE_USAGE)); + } + Index targetTab = ParserUtil.parseTab(argMultimap.getValue(PREFIX_TAB).get()); + return new TabCommand(targetTab); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean isPrefixPresent(ArgumentMultimap argumentMultimap, Prefix prefix) { + return argumentMultimap.getValue(prefix).isPresent(); + } +} diff --git a/src/main/java/trackr/logic/parser/TrackrParser.java b/src/main/java/trackr/logic/parser/TrackrParser.java new file mode 100644 index 00000000000..6d74cae860a --- /dev/null +++ b/src/main/java/trackr/logic/parser/TrackrParser.java @@ -0,0 +1,282 @@ +package trackr.logic.parser; + +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static trackr.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import trackr.logic.commands.Command; +import trackr.logic.commands.ExitCommand; +import trackr.logic.commands.HelpCommand; +import trackr.logic.commands.TabCommand; +import trackr.logic.commands.UploadCsvCommand; +import trackr.logic.commands.menu.AddMenuItemCommand; +import trackr.logic.commands.menu.ClearMenuItemCommand; +import trackr.logic.commands.menu.DeleteMenuItemCommand; +import trackr.logic.commands.menu.EditMenuItemCommand; +import trackr.logic.commands.menu.FindMenuItemCommand; +import trackr.logic.commands.menu.ListMenuItemCommand; +import trackr.logic.commands.order.AddOrderCommand; +import trackr.logic.commands.order.ClearOrderCommand; +import trackr.logic.commands.order.DeleteOrderCommand; +import trackr.logic.commands.order.EditOrderCommand; +import trackr.logic.commands.order.FindOrderCommand; +import trackr.logic.commands.order.ListOrderCommand; +import trackr.logic.commands.order.SortOrdersCommand; +import trackr.logic.commands.supplier.AddSupplierCommand; +import trackr.logic.commands.supplier.ClearSupplierCommand; +import trackr.logic.commands.supplier.DeleteSupplierCommand; +import trackr.logic.commands.supplier.EditSupplierCommand; +import trackr.logic.commands.supplier.FindSupplierCommand; +import trackr.logic.commands.supplier.ListSupplierCommand; +import trackr.logic.commands.task.AddTaskCommand; +import trackr.logic.commands.task.ClearTaskCommand; +import trackr.logic.commands.task.DeleteTaskCommand; +import trackr.logic.commands.task.EditTaskCommand; +import trackr.logic.commands.task.FindTaskCommand; +import trackr.logic.commands.task.ListTaskCommand; +import trackr.logic.commands.task.SortTasksCommand; +import trackr.logic.parser.exceptions.ParseException; +import trackr.logic.parser.menu.AddMenuItemCommandParser; +import trackr.logic.parser.menu.DeleteMenuItemCommandParser; +import trackr.logic.parser.menu.EditMenuItemCommandParser; +import trackr.logic.parser.menu.FindMenuItemCommandParser; +import trackr.logic.parser.order.AddOrderCommandParser; +import trackr.logic.parser.order.DeleteOrderCommandParser; +import trackr.logic.parser.order.EditOrderCommandParser; +import trackr.logic.parser.order.FindOrderCommandParser; +import trackr.logic.parser.order.SortOrdersCommandParser; +import trackr.logic.parser.supplier.AddSupplierCommandParser; +import trackr.logic.parser.supplier.DeleteSupplierCommandParser; +import trackr.logic.parser.supplier.EditSupplierCommandParser; +import trackr.logic.parser.supplier.FindSupplierCommandParser; +import trackr.logic.parser.task.AddTaskCommandParser; +import trackr.logic.parser.task.DeleteTaskCommandParser; +import trackr.logic.parser.task.EditTaskCommandParser; +import trackr.logic.parser.task.FindTaskCommandParser; +import trackr.logic.parser.task.SortTasksCommandParser; + +/** + * Parses user input. + */ +public class TrackrParser { + + /** + * Used for initial separation of command word and args. + */ + private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); + + //Solution below is adapted from AB3 with some parts reused from AB3 with modification + /** + * 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) { + + //@@author chongweiguan-reused + case AddOrderCommand.COMMAND_WORD: + case AddOrderCommand.COMMAND_WORD_SHORTCUT: + return new AddOrderCommandParser().parse(arguments); + //@@author + + case AddSupplierCommand.COMMAND_WORD: + case AddSupplierCommand.COMMAND_WORD_SHORTCUT: + return new AddSupplierCommandParser().parse(arguments); + + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + case AddTaskCommand.COMMAND_WORD: + case AddTaskCommand.COMMAND_WORD_SHORTCUT: + return new AddTaskCommandParser().parse(arguments); + //@@author + + case AddMenuItemCommand.COMMAND_WORD: + case AddMenuItemCommand.COMMAND_WORD_SHORTCUT: + return new AddMenuItemCommandParser().parse(arguments); + + case EditSupplierCommand.COMMAND_WORD: + case EditSupplierCommand.COMMAND_WORD_SHORTCUT: + return new EditSupplierCommandParser().parse(arguments); + + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + case EditTaskCommand.COMMAND_WORD: + case EditTaskCommand.COMMAND_WORD_SHORTCUT: + return new EditTaskCommandParser().parse(arguments); + //@@author + + case EditMenuItemCommand.COMMAND_WORD: + case EditMenuItemCommand.COMMAND_WORD_SHORTCUT: + return new EditMenuItemCommandParser().parse(arguments); + + case EditOrderCommand.COMMAND_WORD: + case EditOrderCommand.COMMAND_WORD_SHORTCUT: + return new EditOrderCommandParser().parse(arguments); + + case DeleteSupplierCommand.COMMAND_WORD: + case DeleteSupplierCommand.COMMAND_WORD_SHORTCUT: + return new DeleteSupplierCommandParser().parse(arguments); + + case DeleteTaskCommand.COMMAND_WORD: + case DeleteTaskCommand.COMMAND_WORD_SHORTCUT: + return new DeleteTaskCommandParser().parse(arguments); + + case DeleteMenuItemCommand.COMMAND_WORD: + case DeleteMenuItemCommand.COMMAND_WORD_SHORTCUT: + return new DeleteMenuItemCommandParser().parse(arguments); + + //@@author chongweiguan-reused + case DeleteOrderCommand.COMMAND_WORD: + case DeleteOrderCommand.COMMAND_WORD_SHORTCUT: + return new DeleteOrderCommandParser().parse(arguments); + //@@author + + case ClearSupplierCommand.COMMAND_WORD: + case ClearSupplierCommand.COMMAND_WORD_SHORTCUT: + return new ClearSupplierCommand(); + + case ClearTaskCommand.COMMAND_WORD: + case ClearTaskCommand.COMMAND_WORD_SHORTCUT: + return new ClearTaskCommand(); + + case ClearMenuItemCommand.COMMAND_WORD: + case ClearMenuItemCommand.COMMAND_WORD_SHORTCUT: + return new ClearMenuItemCommand(); + + case ClearOrderCommand.COMMAND_WORD: + case ClearOrderCommand.COMMAND_WORD_SHORTCUT: + return new ClearOrderCommand(); + + case FindSupplierCommand.COMMAND_WORD: + case FindSupplierCommand.COMMAND_WORD_SHORTCUT: + return new FindSupplierCommandParser().parse(arguments); + + case FindTaskCommand.COMMAND_WORD: + case FindTaskCommand.COMMAND_WORD_SHORTCUT: + return new FindTaskCommandParser().parse(arguments); + + case FindMenuItemCommand.COMMAND_WORD: + case FindMenuItemCommand.COMMAND_WORD_SHORTCUT: + return new FindMenuItemCommandParser().parse(arguments); + + case FindOrderCommand.COMMAND_WORD: + case FindOrderCommand.COMMAND_WORD_SHORTCUT: + return new FindOrderCommandParser().parse(arguments); + + case ListSupplierCommand.COMMAND_WORD: + case ListSupplierCommand.COMMAND_WORD_SHORTCUT: + return new ListSupplierCommand(); + + case ListOrderCommand.COMMAND_WORD: + case ListOrderCommand.COMMAND_WORD_SHORTCUT: + return new ListOrderCommand(); + + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + case ListTaskCommand.COMMAND_WORD: + case ListTaskCommand.COMMAND_WORD_SHORTCUT: + return new ListTaskCommand(); + //@@author + + case ListMenuItemCommand.COMMAND_WORD: + case ListMenuItemCommand.COMMAND_WORD_SHORTCUT: + return new ListMenuItemCommand(); + + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + case SortTasksCommand.COMMAND_WORD: + case SortTasksCommand.COMMAND_WORD_SHORTCUT: + return new SortTasksCommandParser().parse(arguments); + + case SortOrdersCommand.COMMAND_WORD: + case SortOrdersCommand.COMMAND_WORD_SHORTCUT: + return new SortOrdersCommandParser().parse(arguments); + //@@author + + case TabCommand.COMMAND_WORD: + return new TabCommandParser().parse(arguments); + + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); + + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); + + //@@author chongweiguan-reused + case UploadCsvCommand.COMMAND_WORD: + return new UploadCsvCommandParser().parse(arguments); + //@@author + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } + + /** + * Returns a String representation of the list the command is working on. + * + * @param userInput The given user command input. + * @return The string representation of the list name. + * @throws ParseException If user input is invalid and cannot be parsed. + */ + public static String getModel(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"); + switch (commandWord) { + + case ClearOrderCommand.COMMAND_WORD: + case ClearOrderCommand.COMMAND_WORD_SHORTCUT: + case FindOrderCommand.COMMAND_WORD: + case FindOrderCommand.COMMAND_WORD_SHORTCUT: + case ListOrderCommand.COMMAND_WORD: + case ListOrderCommand.COMMAND_WORD_SHORTCUT: + return "ORDERS"; + + case ClearSupplierCommand.COMMAND_WORD: + case ClearSupplierCommand.COMMAND_WORD_SHORTCUT: + case FindSupplierCommand.COMMAND_WORD: + case FindSupplierCommand.COMMAND_WORD_SHORTCUT: + case ListSupplierCommand.COMMAND_WORD: + case ListSupplierCommand.COMMAND_WORD_SHORTCUT: + return "CONTACTS"; + + case ClearTaskCommand.COMMAND_WORD: + case ClearTaskCommand.COMMAND_WORD_SHORTCUT: + case FindTaskCommand.COMMAND_WORD: + case FindTaskCommand.COMMAND_WORD_SHORTCUT: + case SortTasksCommand.COMMAND_WORD: + case SortTasksCommand.COMMAND_WORD_SHORTCUT: + case ListTaskCommand.COMMAND_WORD: + case ListTaskCommand.COMMAND_WORD_SHORTCUT: + return "TASKS"; + + case ClearMenuItemCommand.COMMAND_WORD: + case ClearMenuItemCommand.COMMAND_WORD_SHORTCUT: + case FindMenuItemCommand.COMMAND_WORD: + case FindMenuItemCommand.COMMAND_WORD_SHORTCUT: + case ListMenuItemCommand.COMMAND_WORD: + case ListMenuItemCommand.COMMAND_WORD_SHORTCUT: + return "MENU"; + + default: + return "OTHERS"; + } + } +} diff --git a/src/main/java/trackr/logic/parser/UploadCsvCommandParser.java b/src/main/java/trackr/logic/parser/UploadCsvCommandParser.java new file mode 100644 index 00000000000..3dde4ccd341 --- /dev/null +++ b/src/main/java/trackr/logic/parser/UploadCsvCommandParser.java @@ -0,0 +1,226 @@ +package trackr.logic.parser; + +import static trackr.commons.core.Messages.MESSAGE_INVALID_CSV_FILE; +import static trackr.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static trackr.logic.parser.CliSyntax.PREFIX_COST; +import static trackr.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static trackr.logic.parser.CliSyntax.PREFIX_EMAIL; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_ORDERNAME; +import static trackr.logic.parser.CliSyntax.PREFIX_ORDERQUANTITY; +import static trackr.logic.parser.CliSyntax.PREFIX_PHONE; +import static trackr.logic.parser.CliSyntax.PREFIX_PRICE; +import static trackr.logic.parser.CliSyntax.PREFIX_STATUS; +import static trackr.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import trackr.logic.commands.UploadCsvCommand; +import trackr.logic.parser.exceptions.ParseException; + +//@@author chongweiguan +/** + * Parses input arguments and creates a new UploadCsvCommand object. + */ +public class UploadCsvCommandParser implements Parser { + private List listOfCommands = new ArrayList(); + + /** + * Parses the given {@code String} of arguments in the context of the UploadCsvCommand + * and returns an UploadCsvCommand object for execution. + */ + //@@author + public UploadCsvCommand parse(String args) throws ParseException { + String[] raw = args.split(","); + String[] components = Arrays.copyOfRange(raw, 1, raw.length); + for (int i = 0; i < components.length; i++) { + if (components[i].equals("Orders")) { + listOfCommands.addAll(parseOrders(Arrays.copyOfRange(components, i + 1, components.length))); + } else if (components[i].equals("Tasks")) { + listOfCommands.addAll(parseTasks(Arrays.copyOfRange(components, i + 1, components.length))); + } else if (components[i].equals("Suppliers")) { + listOfCommands.addAll(parseSuppliers(Arrays.copyOfRange(components, i + 1, components.length))); + } else if (components[i].equals("MenuItems")) { + listOfCommands.addAll(parseItems(Arrays.copyOfRange(components, i + 1, components.length))); + } + } + return new UploadCsvCommand(listOfCommands); + } + + private List parseOrders(String[] components) throws ParseException { + List orderCommands = new ArrayList(); + List pattern = new ArrayList(); + for (int i = 0; i < 7; i++) { + switch(components[i]) { + case "OrderName": + pattern.add(PREFIX_ORDERNAME.getPrefix()); + break; + case "Quantity": + pattern.add(PREFIX_ORDERQUANTITY.getPrefix()); + break; + case "Deadline": + pattern.add(PREFIX_DEADLINE.getPrefix()); + break; + case "Status": + pattern.add(PREFIX_STATUS.getPrefix()); + break; + case "CustomerName": + pattern.add(PREFIX_NAME.getPrefix()); + break; + case "CustomerPhone": + pattern.add(PREFIX_PHONE.getPrefix()); + break; + case "CustomerAddress": + pattern.add(PREFIX_ADDRESS.getPrefix()); + break; + default: + throw new ParseException(MESSAGE_INVALID_CSV_FILE); + } + } + + String orderCommand = "add_order "; + for (int i = 7; i < components.length; i++) { + if (components[i] == null || components[i].equals("Suppliers") + || components[i].equals("Tasks") || components[i].equals("MenuItems")) { + break; + } else if (i % 7 == 6) { + if (!components[i].equals("-")) { + orderCommand = orderCommand + pattern.get(i % 7) + components[i]; + } + orderCommands.add(orderCommand); + orderCommand = "add_order "; + } else if (!components[i].equals("-")) { + orderCommand = orderCommand + pattern.get(i % 7) + components[i] + " "; + } else { + throw new ParseException(MESSAGE_INVALID_CSV_FILE); + } + } + return orderCommands; + } + + private List parseTasks(String[] components) throws ParseException { + List taskCommands = new ArrayList(); + List pattern = new ArrayList(); + for (int i = 0; i < 3; i++) { + switch(components[i]) { + case "TaskName": + pattern.add(PREFIX_NAME.getPrefix()); + break; + case "Deadline": + pattern.add(PREFIX_DEADLINE.getPrefix()); + break; + case "Status": + pattern.add(PREFIX_STATUS.getPrefix()); + break; + default: + throw new ParseException(MESSAGE_INVALID_CSV_FILE); + } + } + + String taskCommand = "add_task "; + for (int i = 3; i < components.length; i++) { + if (components[i] == null || components[i].equals("Suppliers") + || components[i].equals("Orders") || components[i].equals("MenuItems")) { + break; + } else if (i % 3 == 2) { + if (!components[i].equals("-")) { + taskCommand = taskCommand + pattern.get(i % 3) + components[i]; + } + taskCommands.add(taskCommand); + taskCommand = "add_task "; + } else if (!components[i].equals("-")) { + taskCommand = taskCommand + pattern.get(i % 3) + components[i] + " "; + } else { + throw new ParseException(MESSAGE_INVALID_CSV_FILE); + } + } + return taskCommands; + } + + private List parseSuppliers(String[] components) throws ParseException { + List supplierCommands = new ArrayList(); + List pattern = new ArrayList(); + for (int i = 0; i < 5; i++) { + switch(components[i]) { + case "Name": + pattern.add(PREFIX_NAME.getPrefix()); + break; + case "Phone": + pattern.add(PREFIX_PHONE.getPrefix()); + break; + case "Email": + pattern.add(PREFIX_EMAIL.getPrefix()); + break; + case "Address": + pattern.add(PREFIX_ADDRESS.getPrefix()); + break; + case "Tag": + pattern.add(PREFIX_TAG.getPrefix()); + break; + default: + throw new ParseException(MESSAGE_INVALID_CSV_FILE); + } + } + + String supplierCommand = "add_supplier "; + for (int i = 5; i < components.length; i++) { + if (components[i] == null || components[i].equals("Tasks") + || components[i].equals("Orders") || components[i].equals("MenuItems")) { + break; + } else if (i % 5 == 4) { + if (!components[i].equals("-")) { + supplierCommand = supplierCommand + pattern.get(i % 5) + components[i]; + } + supplierCommands.add(supplierCommand); + supplierCommand = "add_supplier "; + } else if (!components[i].equals("-")) { + supplierCommand = supplierCommand + pattern.get(i % 5) + components[i] + " "; + } else { + throw new ParseException(MESSAGE_INVALID_CSV_FILE); + } + } + return supplierCommands; + } + + private List parseItems(String[] components) throws ParseException { + List menuItemsCommands = new ArrayList(); + List pattern = new ArrayList(); + for (int i = 0; i < 3; i++) { + switch(components[i]) { + case "ItemName": + pattern.add(PREFIX_NAME.getPrefix()); + break; + case "Cost": + pattern.add(PREFIX_COST.getPrefix()); + break; + case "Price": + pattern.add(PREFIX_PRICE.getPrefix()); + break; + default: + throw new ParseException(MESSAGE_INVALID_CSV_FILE); + } + } + + String menuItemCommand = "add_item "; + for (int i = 3; i < components.length; i++) { + if (components[i] == null || components[i].equals("Tasks") + || components[i].equals("Orders") || components[i].equals("Suppliers")) { + break; + } else if (i % 3 == 2) { + if (!components[i].equals("-")) { + menuItemCommand = menuItemCommand + pattern.get(i % 3) + components[i]; + } + menuItemsCommands.add(menuItemCommand); + menuItemCommand = "add_item "; + } else if (!components[i].equals("-")) { + menuItemCommand = menuItemCommand + pattern.get(i % 3) + components[i] + " "; + } else { + throw new ParseException(MESSAGE_INVALID_CSV_FILE); + } + } + return menuItemsCommands; + } + //@@author +} diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/trackr/logic/parser/exceptions/ParseException.java similarity index 73% rename from src/main/java/seedu/address/logic/parser/exceptions/ParseException.java rename to src/main/java/trackr/logic/parser/exceptions/ParseException.java index 158a1a54c1c..0dc6ed8b698 100644 --- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java +++ b/src/main/java/trackr/logic/parser/exceptions/ParseException.java @@ -1,6 +1,6 @@ -package seedu.address.logic.parser.exceptions; +package trackr.logic.parser.exceptions; -import seedu.address.commons.exceptions.IllegalValueException; +import trackr.commons.exceptions.IllegalValueException; /** * Represents a parse error encountered by a parser. diff --git a/src/main/java/trackr/logic/parser/menu/AddMenuItemCommandParser.java b/src/main/java/trackr/logic/parser/menu/AddMenuItemCommandParser.java new file mode 100644 index 00000000000..cf3fc32a13f --- /dev/null +++ b/src/main/java/trackr/logic/parser/menu/AddMenuItemCommandParser.java @@ -0,0 +1,59 @@ +package trackr.logic.parser.menu; + +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static trackr.logic.parser.CliSyntax.PREFIX_COST; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_PRICE; + +import java.util.stream.Stream; + +import trackr.logic.commands.menu.AddMenuItemCommand; +import trackr.logic.parser.ArgumentMultimap; +import trackr.logic.parser.ArgumentTokenizer; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.Prefix; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.menu.ItemCost; +import trackr.model.menu.ItemName; +import trackr.model.menu.ItemSellingPrice; +import trackr.model.menu.MenuItem; + +/** + * Parses input arguments and creates a new AddMenuItemCommand object. + */ +//@@author changgittyhub-reused +public class AddMenuItemCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddMenuItemCommand + * and returns an AddMenuItemCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public AddMenuItemCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PRICE, PREFIX_COST); + + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_PRICE, PREFIX_COST) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddMenuItemCommand.MESSAGE_USAGE)); + } + + ItemName itemName = ParserUtil.parseItemName(argMultimap.getValue(PREFIX_NAME).get()); + ItemSellingPrice itemPrice = ParserUtil.parseItemPrice(argMultimap.getValue(PREFIX_PRICE).get()); + ItemCost itemCost = ParserUtil.parseItemCost(argMultimap.getValue(PREFIX_COST).get()); + MenuItem menuItem = new MenuItem(itemName, itemPrice, itemCost); + + return new AddMenuItemCommand(menuItem); + } + + /** + * 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/trackr/logic/parser/menu/DeleteMenuItemCommandParser.java b/src/main/java/trackr/logic/parser/menu/DeleteMenuItemCommandParser.java new file mode 100644 index 00000000000..a2a7a522a05 --- /dev/null +++ b/src/main/java/trackr/logic/parser/menu/DeleteMenuItemCommandParser.java @@ -0,0 +1,33 @@ +package trackr.logic.parser.menu; + +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.menu.DeleteMenuItemCommand; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteMenuItemCommand object. + */ +//@@author changgittyhub-reused +public class DeleteMenuItemCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteMenuItemCommand + * and returns a DeleteMenuItemCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public DeleteMenuItemCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteMenuItemCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteMenuItemCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/trackr/logic/parser/menu/EditMenuItemCommandParser.java b/src/main/java/trackr/logic/parser/menu/EditMenuItemCommandParser.java new file mode 100644 index 00000000000..4980c248577 --- /dev/null +++ b/src/main/java/trackr/logic/parser/menu/EditMenuItemCommandParser.java @@ -0,0 +1,66 @@ +package trackr.logic.parser.menu; + + +import static java.util.Objects.requireNonNull; +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static trackr.logic.parser.CliSyntax.PREFIX_COST; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_PRICE; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.menu.EditMenuItemCommand; +import trackr.logic.parser.ArgumentMultimap; +import trackr.logic.parser.ArgumentTokenizer; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.menu.MenuItemDescriptor; + +/** + * Parses input arguments and creates a new EditMenuItemCommand object. + */ +//@@author changgittyhub-reused +public class EditMenuItemCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditMenuItemCommand + * and returns an EditMenuItemCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public EditMenuItemCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PRICE, PREFIX_COST); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditMenuItemCommand.MESSAGE_USAGE), pe); + } + + MenuItemDescriptor editMenuItemDescriptor = new MenuItemDescriptor(); + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + editMenuItemDescriptor.setItemName( + ParserUtil.parseItemName(argMultimap.getValue(PREFIX_NAME).get())); + } + if (argMultimap.getValue(PREFIX_PRICE).isPresent()) { + editMenuItemDescriptor.setItemPrice( + ParserUtil.parseItemPrice(argMultimap.getValue(PREFIX_PRICE).get())); + } + if (argMultimap.getValue(PREFIX_COST).isPresent()) { + editMenuItemDescriptor.setItemCost( + ParserUtil.parseItemCost(argMultimap.getValue(PREFIX_COST).get())); + } + + if (!editMenuItemDescriptor.isAnyFieldNonNull()) { + throw new ParseException(EditMenuItemCommand.MESSAGE_NOT_EDITED); + } + + return new EditMenuItemCommand(index, editMenuItemDescriptor); + } + +} diff --git a/src/main/java/trackr/logic/parser/menu/FindMenuItemCommandParser.java b/src/main/java/trackr/logic/parser/menu/FindMenuItemCommandParser.java new file mode 100644 index 00000000000..29c3c471863 --- /dev/null +++ b/src/main/java/trackr/logic/parser/menu/FindMenuItemCommandParser.java @@ -0,0 +1,35 @@ +package trackr.logic.parser.menu; + +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import trackr.logic.commands.menu.FindMenuItemCommand; +import trackr.logic.parser.Parser; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.menu.ItemNameContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindMenuItemCommand object. + */ +//@@author changgittyhub-reused +public class FindMenuItemCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindMenuItemCommand + * and returns a FindMenuItemCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format. + */ + public FindMenuItemCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindMenuItemCommand.MESSAGE_USAGE)); + } + + String[] nameKeywords = trimmedArgs.split("\\s+"); + + return new FindMenuItemCommand(new ItemNameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + } +} diff --git a/src/main/java/trackr/logic/parser/order/AddOrderCommandParser.java b/src/main/java/trackr/logic/parser/order/AddOrderCommandParser.java new file mode 100644 index 00000000000..637299b077c --- /dev/null +++ b/src/main/java/trackr/logic/parser/order/AddOrderCommandParser.java @@ -0,0 +1,75 @@ +package trackr.logic.parser.order; + +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static trackr.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static trackr.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_ORDERNAME; +import static trackr.logic.parser.CliSyntax.PREFIX_ORDERQUANTITY; +import static trackr.logic.parser.CliSyntax.PREFIX_PHONE; +import static trackr.logic.parser.CliSyntax.PREFIX_STATUS; + +import java.util.stream.Stream; + +import trackr.logic.commands.order.AddOrderCommand; +import trackr.logic.parser.ArgumentMultimap; +import trackr.logic.parser.ArgumentTokenizer; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.Prefix; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.order.Order; +import trackr.model.order.OrderDeadline; +import trackr.model.order.OrderName; +import trackr.model.order.OrderQuantity; +import trackr.model.order.OrderStatus; +import trackr.model.person.Customer; +import trackr.model.person.PersonAddress; +import trackr.model.person.PersonName; +import trackr.model.person.PersonPhone; + +//@@author chongweiguan-reused +/** + * Parses input arguments and creates a new AddOrderCommand object. + */ +public class AddOrderCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the AddOrderCommand + * and returns an AddOrderCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public AddOrderCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_ORDERNAME, PREFIX_ORDERQUANTITY, + PREFIX_DEADLINE, PREFIX_STATUS, PREFIX_NAME, PREFIX_PHONE, PREFIX_ADDRESS); + + if (!arePrefixesPresent(argMultimap, PREFIX_ORDERNAME, PREFIX_ORDERQUANTITY, PREFIX_DEADLINE, + PREFIX_NAME, PREFIX_PHONE, PREFIX_ADDRESS) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddOrderCommand.MESSAGE_USAGE)); + } + + OrderName orderName = ParserUtil.parseOrderName(argMultimap.getValue(PREFIX_ORDERNAME).get()); + OrderQuantity orderQuantity = ParserUtil.parseOrderQuantity(argMultimap.getValue(PREFIX_ORDERQUANTITY).get()); + OrderDeadline orderDeadline = ParserUtil.parseOrderDeadline(argMultimap.getValue(PREFIX_DEADLINE).get()); + OrderStatus orderStatus = ParserUtil.parseOrderStatus(argMultimap.getValue(PREFIX_STATUS)); + PersonName customerName = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + PersonPhone customerPhone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); + PersonAddress customerAddress = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()); + + Customer customer = new Customer(customerName, customerPhone, customerAddress); + Order order = new Order(orderName, orderDeadline, orderStatus, orderQuantity, customer); + + return new AddOrderCommand(order); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + //@@author +} diff --git a/src/main/java/trackr/logic/parser/order/DeleteOrderCommandParser.java b/src/main/java/trackr/logic/parser/order/DeleteOrderCommandParser.java new file mode 100644 index 00000000000..6c24b28bf58 --- /dev/null +++ b/src/main/java/trackr/logic/parser/order/DeleteOrderCommandParser.java @@ -0,0 +1,32 @@ +package trackr.logic.parser.order; + +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.order.DeleteOrderCommand; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.exceptions.ParseException; + +//@@author chongweiguan-reused +/** + * Parses input arguments and creates a new DeleteOrderCommand object. + */ +public class DeleteOrderCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the DeleteOrderCommand + * and returns a DeleteOrderCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public DeleteOrderCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteOrderCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteOrderCommand.MESSAGE_USAGE), pe); + } + } + //@@author +} diff --git a/src/main/java/trackr/logic/parser/order/EditOrderCommandParser.java b/src/main/java/trackr/logic/parser/order/EditOrderCommandParser.java new file mode 100644 index 00000000000..5644d74d2c0 --- /dev/null +++ b/src/main/java/trackr/logic/parser/order/EditOrderCommandParser.java @@ -0,0 +1,90 @@ +package trackr.logic.parser.order; + + +import static java.util.Objects.requireNonNull; +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static trackr.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static trackr.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_ORDERNAME; +import static trackr.logic.parser.CliSyntax.PREFIX_ORDERQUANTITY; +import static trackr.logic.parser.CliSyntax.PREFIX_PHONE; +import static trackr.logic.parser.CliSyntax.PREFIX_STATUS; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.order.EditOrderCommand; +import trackr.logic.parser.ArgumentMultimap; +import trackr.logic.parser.ArgumentTokenizer; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.order.OrderDescriptor; + +/** + * Parses input arguments and creates a new EditOrderCommand object. + */ +//@@author changgittyhub-reused +public class EditOrderCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditOrderCommand + * and returns an EditOrderCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public EditOrderCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_ORDERNAME, PREFIX_DEADLINE, + PREFIX_STATUS, PREFIX_ORDERQUANTITY, PREFIX_NAME, PREFIX_PHONE, PREFIX_ADDRESS); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditOrderCommand.MESSAGE_USAGE), pe); + } + + OrderDescriptor editOrderDescriptor = new OrderDescriptor(); + if (argMultimap.getValue(PREFIX_ORDERNAME).isPresent()) { + editOrderDescriptor.setOrderName( + ParserUtil.parseOrderName(argMultimap.getValue(PREFIX_ORDERNAME).get())); + } + if (argMultimap.getValue(PREFIX_DEADLINE).isPresent()) { + editOrderDescriptor.setOrderDeadline( + ParserUtil.parseOrderDeadline(argMultimap.getValue(PREFIX_DEADLINE).get())); + } + if (argMultimap.getValue(PREFIX_STATUS).isPresent()) { + editOrderDescriptor.setOrderStatus( + ParserUtil.parseOrderStatus(argMultimap.getValue(PREFIX_STATUS))); + } + + if (argMultimap.getValue(PREFIX_ORDERQUANTITY).isPresent()) { + editOrderDescriptor.setOrderQuantity( + ParserUtil.parseOrderQuantity(argMultimap.getValue(PREFIX_ORDERQUANTITY).get())); + } + + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + editOrderDescriptor.setCustomerName( + ParserUtil.parseCustomerName(argMultimap.getValue(PREFIX_NAME).get())); + } + + if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { + editOrderDescriptor.setCustomerPhone( + ParserUtil.parseCustomerPhone(argMultimap.getValue(PREFIX_PHONE).get())); + } + + if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { + editOrderDescriptor.setCustomerAddress( + ParserUtil.parseCustomerAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); + } + + if (!editOrderDescriptor.isAnyFieldNonNull()) { + throw new ParseException(EditOrderCommand.MESSAGE_NOT_EDITED); + } + + return new EditOrderCommand(index, editOrderDescriptor); + } +} diff --git a/src/main/java/trackr/logic/parser/order/FindOrderCommandParser.java b/src/main/java/trackr/logic/parser/order/FindOrderCommandParser.java new file mode 100644 index 00000000000..31285bc3875 --- /dev/null +++ b/src/main/java/trackr/logic/parser/order/FindOrderCommandParser.java @@ -0,0 +1,82 @@ +package trackr.logic.parser.order; + +import static java.util.Objects.requireNonNull; +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static trackr.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static trackr.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_ORDERNAME; +import static trackr.logic.parser.CliSyntax.PREFIX_ORDERQUANTITY; +import static trackr.logic.parser.CliSyntax.PREFIX_PHONE; +import static trackr.logic.parser.CliSyntax.PREFIX_STATUS; + +import java.util.Arrays; + +import trackr.logic.commands.order.FindOrderCommand; +import trackr.logic.parser.ArgumentMultimap; +import trackr.logic.parser.ArgumentTokenizer; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.order.OrderContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindOrderCommand object. + */ +//@@author changgittyhub-reused +public class FindOrderCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the FindOrderCommand + * and returns a FindOrderCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public FindOrderCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_ORDERNAME, PREFIX_DEADLINE, + PREFIX_STATUS, PREFIX_ORDERQUANTITY, + PREFIX_NAME, PREFIX_PHONE, PREFIX_ADDRESS); + + OrderContainsKeywordsPredicate predicate = new OrderContainsKeywordsPredicate(); + if (argMultimap.getValue(PREFIX_ORDERNAME).isPresent()) { + String[] orderNameKeywords = argMultimap.getValue(PREFIX_ORDERNAME).get().trim().split("\\s+"); + predicate.setOrderNameKeywords(Arrays.asList(orderNameKeywords)); + } + if (argMultimap.getValue(PREFIX_DEADLINE).isPresent()) { + predicate.setOrderDeadline( + ParserUtil.parseOrderDeadline(argMultimap.getValue(PREFIX_DEADLINE).get())); + } + if (argMultimap.getValue(PREFIX_STATUS).isPresent()) { + predicate.setOrderStatus( + ParserUtil.parseOrderStatus(argMultimap.getValue(PREFIX_STATUS))); + } + if (argMultimap.getValue(PREFIX_ORDERQUANTITY).isPresent()) { + predicate.setOrderQuantity( + ParserUtil.parseOrderQuantity(argMultimap.getValue(PREFIX_ORDERQUANTITY).get())); + } + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + String[] customerNameKeywords = argMultimap.getValue(PREFIX_NAME).get().trim().split("\\s+"); + predicate.setCustomerNameKeywords(Arrays.asList(customerNameKeywords)); + } + + if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { + predicate.setCustomerPhone( + ParserUtil.parseCustomerPhone(argMultimap.getValue(PREFIX_PHONE).get())); + } + + if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { + predicate.setCustomerAddress( + ParserUtil.parseCustomerAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); + } + + + if (!predicate.isAnyFieldPresent()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindOrderCommand.MESSAGE_USAGE)); + } + + return new FindOrderCommand(predicate); + } + +} diff --git a/src/main/java/trackr/logic/parser/order/SortOrdersCommandParser.java b/src/main/java/trackr/logic/parser/order/SortOrdersCommandParser.java new file mode 100644 index 00000000000..4432db364c1 --- /dev/null +++ b/src/main/java/trackr/logic/parser/order/SortOrdersCommandParser.java @@ -0,0 +1,45 @@ +package trackr.logic.parser.order; + +import static java.util.Objects.requireNonNull; +import static trackr.logic.parser.CliSyntax.PREFIX_CRITERIA; + +import trackr.logic.commands.order.SortOrdersCommand; +import trackr.logic.parser.ArgumentMultimap; +import trackr.logic.parser.ArgumentTokenizer; +import trackr.logic.parser.CriteriaEnum; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.order.SortOrdersComparator; + +//@@author HmuuMyatMoe-reused +//Reused from AB3 with minor modifications +/** + * Parses input arguments and creates a new SortOrdersCommand object. + */ +public class SortOrdersCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the SortOrdersCommand + * and returns a SortOrdersCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public SortOrdersCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_CRITERIA); + + SortOrdersComparator comparator = new SortOrdersComparator(); + if (argMultimap.getValue(PREFIX_CRITERIA).isPresent()) { + comparator.setCriteria( + ParserUtil.parseSortingCriteria(argMultimap.getValue(PREFIX_CRITERIA))); + } else { + //sort by both status and deadline by default + comparator.setCriteria(CriteriaEnum.STATUS_AND_DEADLINE); + } + + return new SortOrdersCommand(comparator); + } +} +//@@author diff --git a/src/main/java/trackr/logic/parser/supplier/AddSupplierCommandParser.java b/src/main/java/trackr/logic/parser/supplier/AddSupplierCommandParser.java new file mode 100644 index 00000000000..ebdd9394a33 --- /dev/null +++ b/src/main/java/trackr/logic/parser/supplier/AddSupplierCommandParser.java @@ -0,0 +1,66 @@ +package trackr.logic.parser.supplier; + +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static trackr.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static trackr.logic.parser.CliSyntax.PREFIX_EMAIL; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_PHONE; +import static trackr.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Set; +import java.util.stream.Stream; + +import trackr.logic.commands.supplier.AddSupplierCommand; +import trackr.logic.parser.ArgumentMultimap; +import trackr.logic.parser.ArgumentTokenizer; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.Prefix; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.commons.Tag; +import trackr.model.person.PersonAddress; +import trackr.model.person.PersonEmail; +import trackr.model.person.PersonName; +import trackr.model.person.PersonPhone; +import trackr.model.person.Supplier; + +/** + * Parses input arguments and creates a new AddSupplierCommand object. + */ +public class AddSupplierCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddSupplierCommand + * and returns an AddSupplierCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public AddSupplierCommand 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, AddSupplierCommand.MESSAGE_USAGE)); + } + + PersonName name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + PersonPhone personPhone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); + PersonEmail personEmail = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); + PersonAddress personAddress = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()); + Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); + + Supplier supplier = new Supplier(name, personPhone, personEmail, personAddress, tagList); + + return new AddSupplierCommand(supplier); + } + + /** + * 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/trackr/logic/parser/supplier/DeleteSupplierCommandParser.java b/src/main/java/trackr/logic/parser/supplier/DeleteSupplierCommandParser.java new file mode 100644 index 00000000000..f3bea39bc8f --- /dev/null +++ b/src/main/java/trackr/logic/parser/supplier/DeleteSupplierCommandParser.java @@ -0,0 +1,32 @@ +package trackr.logic.parser.supplier; + +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.supplier.DeleteSupplierCommand; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteSupplierCommand object. + */ +public class DeleteSupplierCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteSupplierCommand + * and returns a DeleteSupplierCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public DeleteSupplierCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteSupplierCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteSupplierCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/trackr/logic/parser/supplier/EditSupplierCommandParser.java b/src/main/java/trackr/logic/parser/supplier/EditSupplierCommandParser.java new file mode 100644 index 00000000000..d621fcb1efc --- /dev/null +++ b/src/main/java/trackr/logic/parser/supplier/EditSupplierCommandParser.java @@ -0,0 +1,88 @@ +package trackr.logic.parser.supplier; + +import static java.util.Objects.requireNonNull; +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static trackr.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static trackr.logic.parser.CliSyntax.PREFIX_EMAIL; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_PHONE; +import static trackr.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.supplier.EditSupplierCommand; +import trackr.logic.parser.ArgumentMultimap; +import trackr.logic.parser.ArgumentTokenizer; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.commons.Tag; +import trackr.model.person.PersonDescriptor; + +/** + * Parses input arguments and creates a new EditSupplierCommand object. + */ +public class EditSupplierCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditSupplierCommand + * and returns an EditSupplierCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public EditSupplierCommand 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, + EditSupplierCommand.MESSAGE_USAGE), pe); + } + + PersonDescriptor editSupplierDescriptor = new PersonDescriptor(); + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + editSupplierDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); + } + if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { + editSupplierDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); + } + if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { + editSupplierDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); + } + if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { + editSupplierDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); + } + parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editSupplierDescriptor::setTags); + + if (!editSupplierDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditSupplierCommand.MESSAGE_NOT_EDITED); + } + + return new EditSupplierCommand(index, editSupplierDescriptor); + } + + /** + * 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/trackr/logic/parser/supplier/FindSupplierCommandParser.java b/src/main/java/trackr/logic/parser/supplier/FindSupplierCommandParser.java new file mode 100644 index 00000000000..c8a2f3fe92c --- /dev/null +++ b/src/main/java/trackr/logic/parser/supplier/FindSupplierCommandParser.java @@ -0,0 +1,34 @@ +package trackr.logic.parser.supplier; + +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import trackr.logic.commands.supplier.FindSupplierCommand; +import trackr.logic.parser.Parser; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.person.PersonNameContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindSupplierCommand object + */ +public class FindSupplierCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindSupplierCommand + * and returns a FindSupplierCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FindSupplierCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindSupplierCommand.MESSAGE_USAGE)); + } + + String[] nameKeywords = trimmedArgs.split("\\s+"); + + return new FindSupplierCommand(new PersonNameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + } + +} diff --git a/src/main/java/trackr/logic/parser/task/AddTaskCommandParser.java b/src/main/java/trackr/logic/parser/task/AddTaskCommandParser.java new file mode 100644 index 00000000000..ae27c1e816e --- /dev/null +++ b/src/main/java/trackr/logic/parser/task/AddTaskCommandParser.java @@ -0,0 +1,61 @@ +package trackr.logic.parser.task; + +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static trackr.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_STATUS; + +import java.util.stream.Stream; + +import trackr.logic.commands.task.AddTaskCommand; +import trackr.logic.parser.ArgumentMultimap; +import trackr.logic.parser.ArgumentTokenizer; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.Prefix; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.task.Task; +import trackr.model.task.TaskDeadline; +import trackr.model.task.TaskName; +import trackr.model.task.TaskStatus; + +//@@author HmuuMyatMoe-reused +//Reused from AB3 with minor modifications +/** + * Parses input arguments and creates a new AddTaskCommand object. + */ +public class AddTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddTaskCommand + * and returns an AddTaskCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public AddTaskCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_DEADLINE, PREFIX_STATUS); + + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_DEADLINE) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTaskCommand.MESSAGE_USAGE)); + } + + TaskName taskName = ParserUtil.parseTaskName(argMultimap.getValue(PREFIX_NAME).get()); + TaskDeadline taskDeadline = ParserUtil.parseTaskDeadline(argMultimap.getValue(PREFIX_DEADLINE).get()); + TaskStatus taskStatus = ParserUtil.parseTaskStatus(argMultimap.getValue(PREFIX_STATUS)); + Task task = new Task(taskName, taskDeadline, taskStatus); + + return new AddTaskCommand(task); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} +//@@author diff --git a/src/main/java/trackr/logic/parser/task/DeleteTaskCommandParser.java b/src/main/java/trackr/logic/parser/task/DeleteTaskCommandParser.java new file mode 100644 index 00000000000..6e119d79fbe --- /dev/null +++ b/src/main/java/trackr/logic/parser/task/DeleteTaskCommandParser.java @@ -0,0 +1,33 @@ +package trackr.logic.parser.task; + +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.task.DeleteTaskCommand; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteTaskCommand object. + */ +//@@author liumc-sg-reused +public class DeleteTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteTaskCommand + * and returns a DeleteTaskCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public DeleteTaskCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteTaskCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTaskCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/trackr/logic/parser/task/EditTaskCommandParser.java b/src/main/java/trackr/logic/parser/task/EditTaskCommandParser.java new file mode 100644 index 00000000000..e421fc80f86 --- /dev/null +++ b/src/main/java/trackr/logic/parser/task/EditTaskCommandParser.java @@ -0,0 +1,68 @@ +package trackr.logic.parser.task; + + +import static java.util.Objects.requireNonNull; +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static trackr.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_STATUS; + +import trackr.commons.core.index.Index; +import trackr.logic.commands.task.EditTaskCommand; +import trackr.logic.parser.ArgumentMultimap; +import trackr.logic.parser.ArgumentTokenizer; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.task.TaskDescriptor; + +//@@author HmuuMyatMoe-reused +//Reused from AB3 with minor modifications +/** + * Parses input arguments and creates a new EditTaskCommand object. + */ +public class EditTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditTaskCommand + * and returns an EditTaskCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public EditTaskCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_DEADLINE, PREFIX_STATUS); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditTaskCommand.MESSAGE_USAGE), pe); + } + + TaskDescriptor editTaskDescriptor = new TaskDescriptor(); + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + editTaskDescriptor.setTaskName( + ParserUtil.parseTaskName(argMultimap.getValue(PREFIX_NAME).get())); + } + if (argMultimap.getValue(PREFIX_DEADLINE).isPresent()) { + editTaskDescriptor.setTaskDeadline( + ParserUtil.parseTaskDeadline(argMultimap.getValue(PREFIX_DEADLINE).get())); + } + if (argMultimap.getValue(PREFIX_STATUS).isPresent()) { + editTaskDescriptor.setTaskStatus( + ParserUtil.parseTaskStatus(argMultimap.getValue(PREFIX_STATUS))); + } + + if (!editTaskDescriptor.isAnyFieldNonNull()) { + throw new ParseException(EditTaskCommand.MESSAGE_NOT_EDITED); + } + + return new EditTaskCommand(index, editTaskDescriptor); + } + +} +//@@author diff --git a/src/main/java/trackr/logic/parser/task/FindTaskCommandParser.java b/src/main/java/trackr/logic/parser/task/FindTaskCommandParser.java new file mode 100644 index 00000000000..4c480b20dc9 --- /dev/null +++ b/src/main/java/trackr/logic/parser/task/FindTaskCommandParser.java @@ -0,0 +1,57 @@ +package trackr.logic.parser.task; + +import static java.util.Objects.requireNonNull; +import static trackr.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static trackr.logic.parser.CliSyntax.PREFIX_DEADLINE; +import static trackr.logic.parser.CliSyntax.PREFIX_NAME; +import static trackr.logic.parser.CliSyntax.PREFIX_STATUS; + +import java.util.Arrays; + +import trackr.logic.commands.task.FindTaskCommand; +import trackr.logic.parser.ArgumentMultimap; +import trackr.logic.parser.ArgumentTokenizer; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.task.TaskContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindTaskCommand object. + */ +//@@author liumc-sg-reused +public class FindTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindTaskCommand + * and returns a FindTaskCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public FindTaskCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_DEADLINE, PREFIX_STATUS); + + TaskContainsKeywordsPredicate predicate = new TaskContainsKeywordsPredicate(); + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + String[] taskNameKeywords = argMultimap.getValue(PREFIX_NAME).get().trim().split("\\s+"); + predicate.setTaskNameKeywords(Arrays.asList(taskNameKeywords)); + } + if (argMultimap.getValue(PREFIX_DEADLINE).isPresent()) { + predicate.setTaskDeadline( + ParserUtil.parseTaskDeadline(argMultimap.getValue(PREFIX_DEADLINE).get())); + } + if (argMultimap.getValue(PREFIX_STATUS).isPresent()) { + predicate.setTaskStatus( + ParserUtil.parseTaskStatus(argMultimap.getValue(PREFIX_STATUS))); + } + + if (!predicate.isAnyFieldPresent()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindTaskCommand.MESSAGE_USAGE)); + } + + return new FindTaskCommand(predicate); + } +} diff --git a/src/main/java/trackr/logic/parser/task/SortTasksCommandParser.java b/src/main/java/trackr/logic/parser/task/SortTasksCommandParser.java new file mode 100644 index 00000000000..f33b03e4bbc --- /dev/null +++ b/src/main/java/trackr/logic/parser/task/SortTasksCommandParser.java @@ -0,0 +1,45 @@ +package trackr.logic.parser.task; + +import static java.util.Objects.requireNonNull; +import static trackr.logic.parser.CliSyntax.PREFIX_CRITERIA; + +import trackr.logic.commands.task.SortTasksCommand; +import trackr.logic.parser.ArgumentMultimap; +import trackr.logic.parser.ArgumentTokenizer; +import trackr.logic.parser.CriteriaEnum; +import trackr.logic.parser.Parser; +import trackr.logic.parser.ParserUtil; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.task.SortTasksComparator; + +//@@author HmuuMyatMoe-reused +//Reused from AB3 with minor modifications +/** + * Parses input arguments and creates a new SortTasksCommand object. + */ +public class SortTasksCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the SortTasksCommand + * and returns a SortTasksCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format. + */ + public SortTasksCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_CRITERIA); + + SortTasksComparator comparator = new SortTasksComparator(); + if (argMultimap.getValue(PREFIX_CRITERIA).isPresent()) { + comparator.setCriteria( + ParserUtil.parseSortingCriteria(argMultimap.getValue(PREFIX_CRITERIA))); + } else { + //sort by both status and deadline by default + comparator.setCriteria(CriteriaEnum.STATUS_AND_DEADLINE); + } + + return new SortTasksCommand(comparator); + } +} +//@@author diff --git a/src/main/java/trackr/model/Menu.java b/src/main/java/trackr/model/Menu.java new file mode 100644 index 00000000000..8ef0e48ff7f --- /dev/null +++ b/src/main/java/trackr/model/Menu.java @@ -0,0 +1,22 @@ +package trackr.model; + +import trackr.model.item.ItemList; +import trackr.model.menu.MenuItem; + +/** + * Wraps all data at the menu item-list level. + * Duplicates are not allowed (by .isSameItem comparison). + */ +public class Menu extends ItemList implements ReadOnlyMenu { + + public Menu() { + super(); + } + + /** + * Creates a Menu using the Menu Items in the {@code toBeCopied}. + */ + public Menu(ReadOnlyMenu toBeCopied) { + super(toBeCopied); + } +} diff --git a/src/main/java/trackr/model/Model.java b/src/main/java/trackr/model/Model.java new file mode 100644 index 00000000000..49b907a2efa --- /dev/null +++ b/src/main/java/trackr/model/Model.java @@ -0,0 +1,200 @@ +package trackr.model; + +import java.nio.file.Path; +import java.util.Comparator; +import java.util.function.Predicate; + +import javafx.collections.ObservableList; +import trackr.commons.core.GuiSettings; +import trackr.model.item.Item; +import trackr.model.item.ReadOnlyItemList; +import trackr.model.menu.ItemProfit; +import trackr.model.menu.ItemSellingPrice; +import trackr.model.menu.MenuItem; +import trackr.model.order.Order; +import trackr.model.person.Supplier; +import trackr.model.task.Task; + +/** + * The API of the Model component. + */ +public interface Model { + + /** + * {@code Predicate} that always evaluate to true + */ + Predicate PREDICATE_SHOW_ALL_ITEMS = unused -> true; + + // ================================================= User Prefs ================================================= + + /** + * Replaces user prefs data with the data in {@code userPrefs}. + */ + void setUserPrefs(ReadOnlyUserPrefs userPrefs); + + /** + * Returns the user prefs. + */ + ReadOnlyUserPrefs getUserPrefs(); + + /** + * Returns the user prefs' GUI settings. + */ + GuiSettings getGuiSettings(); + + /** + * Sets the user prefs' GUI settings. + */ + void setGuiSettings(GuiSettings guiSettings); + + //@@author liumc-sg-reused + /** + * Returns the user prefs' trackr file path. + */ + //@@author liumc-sg-reused + Path getTrackrFilePath(); + //@@author + + /** + * Sets the user prefs' trackr file path. + */ + //@@author liumc-sg-reused + void setTrackrFilePath(Path trackrFilePath); + //@@author + + // =================================================== Item ======================================================= + + /** + * Sets the item list for the specified {@code ModelEnum}. + */ + //@@author liumc-sg-reused + void setItemList(ModelEnum modelEnum); + //@@author + + /** + * Returns an unmodifiable view of the item list for the specified {@code ModelEnum}. + * @return an unmodifiable view of the item list for the specified {@code ModelEnum}. + */ + //@@author liumc-sg-reused + ReadOnlyItemList getItemList(ModelEnum modelEnum); + //@@author + + /** + * Returns true if the specified item exists in the item list for the specified {@code ModelEnum}. + * @return true if the specified item exists in the item list for the specified {@code ModelEnum}, false otherwise. + */ + //@@author liumc-sg-reused + boolean hasItem(T item, ModelEnum modelEnum); + //@@author + + /** + * Deletes the specified item from the item list for the specified {@code ModelEnum}. + */ + //@@author liumc-sg-reused + void deleteItem(T item, ModelEnum modelEnum); + //@@author + + /** + * Adds the specified item to the item list for the specified {@code ModelEnum}. + */ + //@@author liumc-sg-reused + void addItem(T item, ModelEnum modelEnum); + //@@author + + /** + * Replaces the specified item in the item list for the specified {@code ModelEnum} with the edited item. + */ + //@@author liumc-sg-reused + void setItem(T item, T itemEdited, ModelEnum modelEnum); + //@@author + + /** + * Returns an {@code ObservableList} of items that have been filtered based on the specified criteria for the + * specified {@code ModelEnum}. + */ + //@@author liumc-sg-reused + ObservableList getFilteredItemList(ModelEnum modelEnum); + //@@author + + /** + * Updates the filtered list of items according to the given {@code predicate}. + * + * @throws NullPointerException If {@code predicate} or {@code modelEnum} is {@code null}. + */ + //@@author liumc-sg-reused + void updateFilteredItemList(Predicate predicate, ModelEnum modelEnum); + //@@author + + + // =================================================== Supplier =================================================== + + /** + * Returns the SupplierList. + */ + ReadOnlySupplierList getSupplierList(); + + /** + * Returns an unmodifiable view of the filtered supplier list. + */ + ObservableList getFilteredSupplierList(); + + /** + * Sorts the filtered order list. + */ + void sortFilteredOrderList(Comparator comparator); + + // ===================================================== Task ===================================================== + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + /** + * Returns the TaskList. + */ + ReadOnlyTaskList getTaskList(); + + /** + * Returns an unmodifiable view of the filtered task list. + */ + ObservableList getFilteredTaskList(); + //@@author + + /** + * Sorts the filtered task list. + */ + void sortFilteredTaskList(Comparator comparator); + + // ================================================= Order ================================================= + + //@@author chongweiguan-reused + /** + * Returns the OrderList + */ + ReadOnlyOrderList getOrderList(); + + /** + * Returns an unmodifiable view of the filtered order list + */ + ObservableList getFilteredOrderList(); + //@@author + + // ===================================================== Menu Item ======================================== + + /** + * Returns the Menu + */ + ReadOnlyMenu getMenu(); + + /** + * Returns an unmodifiable view of the filtered Menu Item list + */ + ObservableList getFilteredMenu(); + + // ===================================================== Calculation ======================================== + + /** Returns cumulative profits */ + //@@author changgittyhub + ItemProfit getTotalProfits(); + + /** Returns cumulative sales/ revenue */ + //@@author changgittyhub + ItemSellingPrice getTotalSales(); +} diff --git a/src/main/java/trackr/model/ModelEnum.java b/src/main/java/trackr/model/ModelEnum.java new file mode 100644 index 00000000000..9ce192e3edd --- /dev/null +++ b/src/main/java/trackr/model/ModelEnum.java @@ -0,0 +1,26 @@ +package trackr.model; + +/** + * Enum representing all the available models. + */ +public enum ModelEnum { + + SUPPLIER("Supplier"), + TASK("Task"), + ORDER("Order"), + CUSTOMER("Customer"), + Item("Item"), + MENUITEM("Menu Item"); + + + private final String strRepresentation; + + ModelEnum(String strRepresentation) { + this.strRepresentation = strRepresentation; + } + + @Override + public String toString() { + return strRepresentation; + } +} diff --git a/src/main/java/trackr/model/ModelManager.java b/src/main/java/trackr/model/ModelManager.java new file mode 100644 index 00000000000..c776dccbf17 --- /dev/null +++ b/src/main/java/trackr/model/ModelManager.java @@ -0,0 +1,429 @@ +package trackr.model; + +import static java.util.Objects.requireNonNull; +import static trackr.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 trackr.commons.core.GuiSettings; +import trackr.commons.core.LogsCenter; +import trackr.model.item.Item; +import trackr.model.item.ReadOnlyItemList; +import trackr.model.menu.ItemProfit; +import trackr.model.menu.ItemSellingPrice; +import trackr.model.menu.MenuItem; +import trackr.model.order.Order; +import trackr.model.person.Supplier; +import trackr.model.task.Task; + +/** + * Represents the in-memory model of trackr data. + */ +public class ModelManager implements Model { + + //@@author liumc-sg-reused + private static final Logger logger = LogsCenter.getLogger(ModelManager.class); + private final SupplierList supplierList; + private final TaskList taskList; + private final Menu menu; + private final OrderList orderList; + private final UserPrefs userPrefs; + private final FilteredList filteredSuppliers; + private final FilteredList filteredTasks; + private final FilteredList filteredOrders; + private final FilteredList filteredMenuItems; + + /** + * Initializes a ModelManager with the given supplier list, taskList and userPrefs. + */ + public ModelManager(ReadOnlySupplierList supplierList, ReadOnlyTaskList taskList, ReadOnlyMenu menu, + ReadOnlyOrderList orderList, ReadOnlyUserPrefs userPrefs) { + requireAllNonNull(supplierList, taskList, orderList, userPrefs); + + logger.fine("Initializing with supplier list: " + supplierList + + " and task list: " + taskList + + " and menu: " + menu + + " and order list: " + orderList + + " and user prefs " + userPrefs); + + this.supplierList = new SupplierList(supplierList); + this.taskList = new TaskList(taskList); + this.menu = new Menu(menu); + this.orderList = new OrderList(orderList); + this.userPrefs = new UserPrefs(userPrefs); + filteredSuppliers = new FilteredList<>(this.supplierList.getItemList()); + filteredTasks = new FilteredList<>(this.taskList.getItemList()); + filteredOrders = new FilteredList<>(this.orderList.getItemList()); + filteredMenuItems = new FilteredList<>(this.menu.getItemList()); + + } + + public ModelManager() { + this(new SupplierList(), new TaskList(), new Menu(), new OrderList(), new UserPrefs()); + } + + //=========== UserPrefs ================================================================================== + + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + requireNonNull(userPrefs); + this.userPrefs.resetData(userPrefs); + } + + @Override + public ReadOnlyUserPrefs getUserPrefs() { + return userPrefs; + } + + @Override + public GuiSettings getGuiSettings() { + return userPrefs.getGuiSettings(); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + requireNonNull(guiSettings); + userPrefs.setGuiSettings(guiSettings); + } + + @Override + public Path getTrackrFilePath() { + return userPrefs.getTrackrFilePath(); + } + + @Override + public void setTrackrFilePath(Path trackrFilePath) { + requireNonNull(trackrFilePath); + userPrefs.setTrackrFilePath(trackrFilePath); + } + + // =================================================== Item ======================================================= + + @Override + public void setItemList(ModelEnum modelEnum) { + switch (modelEnum) { + case SUPPLIER: + this.supplierList.resetData(supplierList); + break; + case TASK: + this.taskList.resetData(taskList); + break; + case MENUITEM: + this.menu.resetData(menu); + break; + case ORDER: + this.orderList.resetData(orderList); + break; + case CUSTOMER: + default: + break; + } + } + + @Override + public ReadOnlyItemList getItemList(ModelEnum modelEnum) { + switch (modelEnum) { + case SUPPLIER: + return supplierList; + case TASK: + return taskList; + case MENUITEM: + return menu; + case ORDER: + return orderList; + case CUSTOMER: + default: + return null; + } + } + + @Override + public boolean hasItem(T item, ModelEnum modelEnum) { + requireNonNull(item); + switch (modelEnum) { + case SUPPLIER: + return supplierList.hasItem((Supplier) item); + case TASK: + return taskList.hasItem((Task) item); + case MENUITEM: + return menu.hasItem((MenuItem) item); + case ORDER: + return orderList.hasItem((Order) item); + case CUSTOMER: + default: + return false; + } + } + + @Override + public void deleteItem(T item, ModelEnum modelEnum) { + switch (modelEnum) { + case SUPPLIER: + supplierList.removeItem((Supplier) item); + break; + case TASK: + taskList.removeItem((Task) item); + break; + case MENUITEM: + menu.removeItem((MenuItem) item); + break; + case ORDER: + orderList.removeItem((Order) item); + break; + case CUSTOMER: + default: + break; + } + } + + @Override + public void addItem(T item, ModelEnum modelEnum) { + switch (modelEnum) { + case SUPPLIER: + supplierList.addItem((Supplier) item); + updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS, modelEnum); + break; + case TASK: + taskList.addItem((Task) item); + updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS, modelEnum); + break; + case MENUITEM: + menu.addItem((MenuItem) item); + updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS, modelEnum); + break; + case ORDER: + orderList.addItem((Order) item); + updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS, modelEnum); + break; + case CUSTOMER: + default: + break; + } + } + + @Override + public void setItem(T item, T itemEdited, ModelEnum modelEnum) { + requireAllNonNull(item, itemEdited); + switch (modelEnum) { + case SUPPLIER: + supplierList.setItem((Supplier) item, (Supplier) itemEdited); + break; + case TASK: + taskList.setItem((Task) item, (Task) itemEdited); + break; + case MENUITEM: + menu.setItem((MenuItem) item, (MenuItem) itemEdited); + break; + case ORDER: + orderList.setItem((Order) item, (Order) itemEdited); + break; + case CUSTOMER: + default: + break; + } + } + + @Override + public FilteredList getFilteredItemList(ModelEnum modelEnum) { + switch (modelEnum) { + case SUPPLIER: + return filteredSuppliers; + case TASK: + return filteredTasks; + case MENUITEM: + return filteredMenuItems; + case ORDER: + return filteredOrders; + case CUSTOMER: + default: + return null; + } + } + + @Override + public void updateFilteredItemList(Predicate predicate, ModelEnum modelEnum) { + requireNonNull(predicate); + switch (modelEnum) { + case SUPPLIER: + filteredSuppliers.setPredicate(predicate); + break; + case TASK: + filteredTasks.setPredicate(predicate); + break; + case MENUITEM: + filteredMenuItems.setPredicate(predicate); + break; + case ORDER: + filteredOrders.setPredicate(predicate); + break; + case CUSTOMER: + default: + break; + } + } + + //@@author + + + //=========== SupplierList - Supplier ============================================================================== + + @Override + public ReadOnlySupplierList getSupplierList() { + return supplierList; + } + + //=========== Filtered Supplier List Accessors ============================================================= + + /** + * Returns an unmodifiable view of the list of {@code Supplier} backed by the internal list of + * {@code versionedSupplierList} + */ + @Override + public ObservableList getFilteredSupplierList() { + return filteredSuppliers; + } + + //@@author liumc-sg-reused + + //=========== TaskList =================================================================================== + + @Override + public ReadOnlyTaskList getTaskList() { + return taskList; + } + + //@@author + + /** + * Sorts and updates the sorted task list. + * + * @param comparator The comparator used to sort the tasks. + */ + @Override + public void sortFilteredTaskList(Comparator comparator) { + requireNonNull(comparator); + taskList.sortItems(comparator); + } + + + //=========== Filtered Task List Accessors =============================================================== + + //@@author liumc-sg-reused + + /** + * Returns an unmodifiable view of the list of {@code Task} backed by the internal list of + * {@code versionedTaskList} + */ + @Override + public ObservableList getFilteredTaskList() { + return filteredTasks; + } + //@@author + + //=========== Menu =================================================================================== + + //@@author changgittyhub-reused + @Override + public ReadOnlyMenu getMenu() { + return menu; + } + + + //=========== Filtered Menu Accessors =============================================================== + + /** + * Returns an unmodifiable view of the list of {@code MenuItem} backed by the internal list of + * {@code versionedMenu} + */ + //@@author changgittyhub-reused + @Override + public ObservableList getFilteredMenu() { + return filteredMenuItems; + } + + //=========== OrderList =================================================================================== + + //@@author chongweiguan-reused + @Override + public ReadOnlyOrderList getOrderList() { + return orderList; + } + + //=========== Filtered Order List Accessors ============================================================= + + /** + * Returns an unmodifiable view of the list of {@code Order} backed by the internal list of + * {@code versionedOrderList} + */ + @Override + public ObservableList getFilteredOrderList() { + return filteredOrders; + } + + /** + * Sort the filtered order list. + * + * @param comparator The comparator used to compare 2 order objects. + */ + @Override + public void sortFilteredOrderList(Comparator comparator) { + requireNonNull(comparator); + orderList.sortItems(comparator); + } + //@@author + + //======================================================================================================== + + @Override + public boolean equals(Object obj) { + // short circuit if same object + if (obj == this) { + return true; + } + + // instanceof handles nulls + if (!(obj instanceof ModelManager)) { + return false; + } + + // state check + ModelManager other = (ModelManager) obj; + return supplierList.equals(other.supplierList) + && taskList.equals(other.taskList) + && menu.equals(other.menu) + && orderList.equals(other.orderList) + && userPrefs.equals(other.userPrefs) + && filteredSuppliers.equals(other.filteredSuppliers) + && filteredTasks.equals(other.filteredTasks) + && filteredMenuItems.equals(other.filteredMenuItems) + && filteredOrders.equals(other.filteredOrders); + } + + //=========== Calculation ============================================================= + + //@@author arkarsg + @Override + public ItemProfit getTotalProfits() { + ObservableList allOrders = this.getFilteredOrderList(); + Double total = allOrders.stream() + .map(x -> x.getTotalProfit().getValue()) + .reduce(0.0, (subTotal, element) -> subTotal + element); + return new ItemProfit(total); + } + + //@@author arkarsg + @Override + public ItemSellingPrice getTotalSales() { + ObservableList allOrders = this.getFilteredOrderList(); + Double total = allOrders.stream() + .map(x -> x.getTotalRevenue().getValue()) + .reduce(0.0, (subTotal, element) -> subTotal + element); + return new ItemSellingPrice(total); + } + +} diff --git a/src/main/java/trackr/model/ObservableTabIndex.java b/src/main/java/trackr/model/ObservableTabIndex.java new file mode 100644 index 00000000000..4909b186168 --- /dev/null +++ b/src/main/java/trackr/model/ObservableTabIndex.java @@ -0,0 +1,26 @@ +package trackr.model; + +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; + +//@@author arkarsg +/** + * Observable value of selected tab that changes upon tab commands. + */ +public class ObservableTabIndex { + private static final SimpleIntegerProperty targetTabIndex = new SimpleIntegerProperty(); + + public static final IntegerProperty valueProperty() { + return targetTabIndex; + } + + public static final void updateToTab(int target) { + targetTabIndex.set(target); + } + + //@@author arkarsg + public static final int getTargetTab() { + return targetTabIndex.getValue(); + } + //@@author +} diff --git a/src/main/java/trackr/model/OrderList.java b/src/main/java/trackr/model/OrderList.java new file mode 100644 index 00000000000..47c97a75fd1 --- /dev/null +++ b/src/main/java/trackr/model/OrderList.java @@ -0,0 +1,22 @@ +package trackr.model; + +import trackr.model.item.ItemList; +import trackr.model.order.Order; + +/** + * Wraps all data at the order-list level. + * Duplicates are not allowed (by .isSameOrder comparison). + */ +public class OrderList extends ItemList implements ReadOnlyOrderList { + + public OrderList() { + super(); + } + + /** + * Creates a OrderList using the Orders in the {@code toBeCopied}. + */ + public OrderList(ReadOnlyOrderList toBeCopied) { + super(toBeCopied); + } +} diff --git a/src/main/java/trackr/model/ReadOnlyMenu.java b/src/main/java/trackr/model/ReadOnlyMenu.java new file mode 100644 index 00000000000..53bac20d8f5 --- /dev/null +++ b/src/main/java/trackr/model/ReadOnlyMenu.java @@ -0,0 +1,11 @@ +package trackr.model; + +import trackr.model.item.ReadOnlyItemList; +import trackr.model.menu.MenuItem; + +/** + * Unmodifiable view of a menu item list. + */ +//@@author changgittyhub-reused +public interface ReadOnlyMenu extends ReadOnlyItemList { +} diff --git a/src/main/java/trackr/model/ReadOnlyOrderList.java b/src/main/java/trackr/model/ReadOnlyOrderList.java new file mode 100644 index 00000000000..b2fecee5835 --- /dev/null +++ b/src/main/java/trackr/model/ReadOnlyOrderList.java @@ -0,0 +1,11 @@ +package trackr.model; + +import trackr.model.item.ReadOnlyItemList; +import trackr.model.order.Order; + +/** + * Unmodifiable view of a order list. + */ +//@@author liumc-sg-reused +public interface ReadOnlyOrderList extends ReadOnlyItemList { +} diff --git a/src/main/java/trackr/model/ReadOnlySupplierList.java b/src/main/java/trackr/model/ReadOnlySupplierList.java new file mode 100644 index 00000000000..3f23cb9938a --- /dev/null +++ b/src/main/java/trackr/model/ReadOnlySupplierList.java @@ -0,0 +1,12 @@ +package trackr.model; + +import trackr.model.item.ReadOnlyItemList; +import trackr.model.person.Supplier; + +/** + * Unmodifiable view of a supplier list. + */ +//@@author liumc-sg-reused +public interface ReadOnlySupplierList extends ReadOnlyItemList { + +} diff --git a/src/main/java/trackr/model/ReadOnlyTaskList.java b/src/main/java/trackr/model/ReadOnlyTaskList.java new file mode 100644 index 00000000000..71efb07576b --- /dev/null +++ b/src/main/java/trackr/model/ReadOnlyTaskList.java @@ -0,0 +1,14 @@ +package trackr.model; + +import trackr.model.item.ReadOnlyItemList; +import trackr.model.task.Task; + +//@@author HmuuMyatMoe-reused +// with minor modifications +/** + * Unmodifiable view of a task list. + */ +//@@author liumc-sg-reused +public interface ReadOnlyTaskList extends ReadOnlyItemList { +} +//@@author diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/trackr/model/ReadOnlyUserPrefs.java similarity index 57% rename from src/main/java/seedu/address/model/ReadOnlyUserPrefs.java rename to src/main/java/trackr/model/ReadOnlyUserPrefs.java index befd58a4c73..172d46c291e 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/trackr/model/ReadOnlyUserPrefs.java @@ -1,8 +1,8 @@ -package seedu.address.model; +package trackr.model; import java.nio.file.Path; -import seedu.address.commons.core.GuiSettings; +import trackr.commons.core.GuiSettings; /** * Unmodifiable view of user prefs. @@ -11,6 +11,7 @@ public interface ReadOnlyUserPrefs { GuiSettings getGuiSettings(); - Path getAddressBookFilePath(); + //@@author + Path getTrackrFilePath(); } diff --git a/src/main/java/trackr/model/SupplierList.java b/src/main/java/trackr/model/SupplierList.java new file mode 100644 index 00000000000..19546e0422f --- /dev/null +++ b/src/main/java/trackr/model/SupplierList.java @@ -0,0 +1,22 @@ +package trackr.model; + +import trackr.model.item.ItemList; +import trackr.model.person.Supplier; + +/** + * Wraps all data at the supplier-list level. + * Duplicates are not allowed (by .isSameItem comparison). + */ +public class SupplierList extends ItemList implements ReadOnlySupplierList { + + public SupplierList() { + super(); + } + + /** + * Creates a SupplierList using the Suppliers in the {@code toBeCopied}. + */ + public SupplierList(ReadOnlySupplierList toBeCopied) { + super(toBeCopied); + } +} diff --git a/src/main/java/trackr/model/TabEnum.java b/src/main/java/trackr/model/TabEnum.java new file mode 100644 index 00000000000..2a4a71d77fa --- /dev/null +++ b/src/main/java/trackr/model/TabEnum.java @@ -0,0 +1,18 @@ +package trackr.model; + +//@@author arkarsg +/** + * Enum representing all tabs. + */ +public enum TabEnum { + HOME, + ORDERS, + TASKS, + CONTACTS, + MENU, + OTHERS; + + public static int getTabIndex(String targetStr) { + return TabEnum.valueOf(targetStr).ordinal(); + } +} diff --git a/src/main/java/trackr/model/TaskList.java b/src/main/java/trackr/model/TaskList.java new file mode 100644 index 00000000000..a0666481693 --- /dev/null +++ b/src/main/java/trackr/model/TaskList.java @@ -0,0 +1,25 @@ +package trackr.model; + +import trackr.model.item.ItemList; +import trackr.model.task.Task; + +//@@author HmuuMyatMoe-reused +//Reused from AB3 with minor modifications +/** + * Wraps all data at the task-list level. + * Duplicates are not allowed (by .isSameItem comparison). + */ +public class TaskList extends ItemList implements ReadOnlyTaskList { + + public TaskList() { + super(); + } + + /** + * Creates a TaskList using the Tasks in the {@code toBeCopied}. + */ + public TaskList(ReadOnlyTaskList toBeCopied) { + super(toBeCopied); + } +} +//@@author diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/trackr/model/UserPrefs.java similarity index 70% rename from src/main/java/seedu/address/model/UserPrefs.java rename to src/main/java/trackr/model/UserPrefs.java index 25a5fd6eab9..b5e1cd5ad82 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/trackr/model/UserPrefs.java @@ -1,4 +1,4 @@ -package seedu.address.model; +package trackr.model; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.nio.file.Paths; import java.util.Objects; -import seedu.address.commons.core.GuiSettings; +import trackr.commons.core.GuiSettings; /** * Represents User's preferences. @@ -14,7 +14,7 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path trackrFilePath = Paths.get("data" , "trackr.json"); /** * Creates a {@code UserPrefs} with default values. @@ -29,14 +29,16 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) { resetData(userPrefs); } + //@@author liumc-sg-reused /** * Resets the existing data of this {@code UserPrefs} with {@code newUserPrefs}. */ public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); - setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setTrackrFilePath(newUserPrefs.getTrackrFilePath()); } + //@@author public GuiSettings getGuiSettings() { return guiSettings; @@ -47,15 +49,17 @@ public void setGuiSettings(GuiSettings guiSettings) { this.guiSettings = guiSettings; } - public Path getAddressBookFilePath() { - return addressBookFilePath; + //@@author liumc-sg-reused + public Path getTrackrFilePath() { + return trackrFilePath; } - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - this.addressBookFilePath = addressBookFilePath; + public void setTrackrFilePath(Path trackrFilePath) { + requireNonNull(trackrFilePath); + this.trackrFilePath = trackrFilePath; } + //@@author @Override public boolean equals(Object other) { if (other == this) { @@ -68,19 +72,19 @@ public boolean equals(Object other) { UserPrefs o = (UserPrefs) other; return guiSettings.equals(o.guiSettings) - && addressBookFilePath.equals(o.addressBookFilePath); + && trackrFilePath.equals(o.trackrFilePath); } @Override public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); + return Objects.hash(guiSettings, trackrFilePath); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Gui Settings : " + guiSettings); - sb.append("\nLocal data file location : " + addressBookFilePath); + sb.append("\nLocal data file location : " + trackrFilePath); return sb.toString(); } diff --git a/src/main/java/trackr/model/commons/Deadline.java b/src/main/java/trackr/model/commons/Deadline.java new file mode 100644 index 00000000000..94da63d0a39 --- /dev/null +++ b/src/main/java/trackr/model/commons/Deadline.java @@ -0,0 +1,104 @@ +package trackr.model.commons; + +import static trackr.commons.util.AppUtil.checkArgument; +import static trackr.commons.util.CollectionUtil.requireAllNonNull; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Represents a deadline in the list. + * Guarantees: immutable; is valid as declared in {@link #isValidDeadline(String)} + */ +public abstract class Deadline { + + public static final String MESSAGE_CONSTRAINTS = + "%s deadline should only contain numeric values " + + "in the format \"DD/MM/YYYY\" and it should not be blank."; + + private static final String VALIDATION_REGEX = "^[0-9]{2}/[0-9]{2}/[0-9]{4}$"; + + private static final DateTimeFormatter DTF_INPUT_DATE = DateTimeFormatter.ofPattern("dd/MM/yyyy"); + private static final DateTimeFormatter DTF_OUTPUT_DATE = DateTimeFormatter.ofPattern("dd LLLL yyyy"); + + private final LocalDate deadline; + + /** + * Constructs a {@code Deadline}. + */ + public Deadline(String deadline, String type) { + requireAllNonNull(deadline, type); + String error = String.format(MESSAGE_CONSTRAINTS, type); + checkArgument(isValidDeadline(deadline), error); + this.deadline = LocalDate.parse(deadline, DTF_INPUT_DATE); + } + + /** + * Checks if a given deadline String conforms to the expected format. + * + * @param test The given string to check. + * @return True if a given string is of valid format, meaning string is of the format "dd/MM/yyyy". + * Returns false otherwise. + */ + public static boolean isValidDeadline(String test) { + if (!test.matches(VALIDATION_REGEX)) { + return false; + } + + try { + LocalDate.parse(test, DTF_INPUT_DATE); + return true; + } catch (DateTimeParseException e) { + return false; + } + } + + /** + * Compare this deadline to a given deadline. + * + * @return 1 if this deadline is after the given deadline, + * -1 if this deadline is before the given status, + * 0 if both deadlines are the same. + */ + public int compare(Deadline other) { + int compareVal = deadline.compareTo(other.deadline); + + if (compareVal == 0) { + return 0; + } else { + return compareVal / Math.abs(compareVal); + } + } + + /** + * Returns the deadline stored in "dd/MM/yyyy" format for json storage. + * + * @return A string representation of the deadline. + */ + public String toJsonString() { + return deadline.format(DTF_INPUT_DATE); + } + + /** + * Returns the deadline stored in "01 JANUARY 2023" format. + * + * @return A string representation of the deadline. + */ + @Override + public String toString() { + return deadline.format(DTF_OUTPUT_DATE); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Deadline // instanceof handles nulls + && deadline.equals(((Deadline) other).deadline)); // state check + } + + @Override + public int hashCode() { + return deadline.hashCode(); + } +} diff --git a/src/main/java/trackr/model/commons/Name.java b/src/main/java/trackr/model/commons/Name.java new file mode 100644 index 00000000000..1cd1a1b1f34 --- /dev/null +++ b/src/main/java/trackr/model/commons/Name.java @@ -0,0 +1,88 @@ +package trackr.model.commons; + +import static trackr.commons.util.AppUtil.checkArgument; +import static trackr.commons.util.CollectionUtil.requireAllNonNull; + +/** + * Represents a name in the list. + * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} + */ +//@@author liumc-sg-reused +public abstract class Name { + + public static final String MESSAGE_CONSTRAINTS = + "%s names should only contain alphanumerical characters and spaces, and it should not be blank"; + + /* + * The first character of the name must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + private static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; + + private final String name; + + /** + * Constructs a {@code Name}. + * + * @param name A valid name. + * @param type Type of name + */ + public Name(String name, String type) { + requireAllNonNull(name, type); + checkArgument(isValidName(name), String.format(MESSAGE_CONSTRAINTS, type)); + this.name = name; + } + + /** + * Checks if a given name String conforms to the expected format. + * + * @param test The given string to check. + * @return True if a given string is a of valid format false otherwise. + */ + public static boolean isValidName(String test) { + return test.matches(VALIDATION_REGEX); + } + + public String getName() { + return name; + } + + //@@author hmuumyatmoe + + /** + * Compare two names lexicographically (ignoring case). + * + * @param other The name to compare this name with. + * @return 1 if this name is lexicographically larger (ignoring case) than the other name, + * -1 if this name is lexicographically smaller (ignoring case) than the other name, + * 0 if both names are lexicographically equal (ignoring case). + */ + //@@author liumc-sg + public int compare(Name other) { + int compareVal = name.compareToIgnoreCase(other.name); + + if (compareVal == 0) { + return 0; + } else { + return compareVal / Math.abs(compareVal); + } + } + + //@@author liumc-sg-reused + @Override + public String toString() { + return name; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Name // instanceof handles nulls + && name.equals(((Name) other).name)); // state check + } + + @Override + public int hashCode() { + return name.hashCode(); + } +} diff --git a/src/main/java/trackr/model/commons/Status.java b/src/main/java/trackr/model/commons/Status.java new file mode 100644 index 00000000000..96c9e013c86 --- /dev/null +++ b/src/main/java/trackr/model/commons/Status.java @@ -0,0 +1,99 @@ +package trackr.model.commons; + +import static trackr.commons.util.AppUtil.checkArgument; +import static trackr.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.HashMap; +import java.util.Map.Entry; + +/** + * Represents a status in the list. + * Guarantees: immutable; is valid as declared in {@link #isValidStatus(String, HashMap)} + */ +public abstract class Status { + + public static final String MESSAGE_CONSTRAINTS_FORMAT = "%s status should only be %s"; + private final HashMap statuses = new HashMap<>(); + + private final String status; + + /** + * Constructs a {@code Status}. + * + * @param status The status string given by user. + * @param type The type of status. (e.g. order stauts, task status, etc.) + * @param statuses The HashMap of valid statues to be created + * and used to check the given status for validity. + */ + public Status(String status, String type, HashMap statuses) { + requireAllNonNull(status, type, statuses); + + for (Entry entry : statuses.entrySet()) { + this.statuses.put(entry.getKey().toUpperCase(), entry.getValue()); + } + + checkArgument(isValidStatus(status, statuses), + String.format(MESSAGE_CONSTRAINTS_FORMAT, type, getStatusMessage())); + this.status = status.toUpperCase(); + } + + /** + * Checks if a given status String conforms to the expected format. + * + * @param test The string to check. + * @param statuses The HashMap of valid string to check the given string with. + * @return Returns true if a given string is of valid format, false otherwise. + */ + public static boolean isValidStatus(String test, HashMap statuses) { + String validationRegex = String.format("^[%s]$", + statuses.keySet().stream().reduce("", (t, e) -> t + e + e.toLowerCase())); + return test.matches(validationRegex); + } + + /** + * Returns a string that represents the types of valid statuses. + * + * @return A String Message that list out the valid statues. + */ + public String getStatusMessage() { + StringBuilder statusMessageBuilder = new StringBuilder(); + for (Entry entry : this.statuses.entrySet()) { + statusMessageBuilder.append(String.format(" `%s` or `%s` for %s,", entry.getKey(), + entry.getKey().toLowerCase(), entry.getValue())); + } + String statusMessage = + statusMessageBuilder.deleteCharAt(statusMessageBuilder.length() - 1).append(".").toString(); + + return statusMessage; + } + + /** + * Compares this status to a given status. + * + * @other The status to compare this status with. + * @return -1, 1 or 0 according to the sorting criteria. + */ + public abstract int compare(Status other); + + public String toJsonString() { + return status; + } + + @Override + public String toString() { + return statuses.get(status); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Status // instanceof handles nulls + && status.equals(((Status) other).status)); // state check + } + + @Override + public int hashCode() { + return status.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/trackr/model/commons/Tag.java similarity index 81% rename from src/main/java/seedu/address/model/tag/Tag.java rename to src/main/java/trackr/model/commons/Tag.java index b0ea7e7dad7..9bdb37b46ba 100644 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ b/src/main/java/trackr/model/commons/Tag.java @@ -1,7 +1,7 @@ -package seedu.address.model.tag; +package trackr.model.commons; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static trackr.commons.util.AppUtil.checkArgument; /** * Represents a Tag in the address book. @@ -26,7 +26,10 @@ public Tag(String tagName) { } /** - * Returns true if a given string is a valid tag name. + * Checks if the given tag name conforms to the expected format. + * + * @param test The given tag name. + * @return True if a given string is of valid format, false otherwise. */ public static boolean isValidTagName(String test) { return test.matches(VALIDATION_REGEX); @@ -47,6 +50,7 @@ public int hashCode() { /** * Format state as text for viewing. */ + @Override public String toString() { return '[' + tagName + ']'; } diff --git a/src/main/java/trackr/model/item/Item.java b/src/main/java/trackr/model/item/Item.java new file mode 100644 index 00000000000..4da13414d91 --- /dev/null +++ b/src/main/java/trackr/model/item/Item.java @@ -0,0 +1,20 @@ +package trackr.model.item; + +import trackr.model.ModelEnum; + +/** + * Generic class that all items extend from. + */ +public abstract class Item { + protected ModelEnum modelEnum; + + public Item(ModelEnum modelEnum) { + this.modelEnum = modelEnum; + } + + public abstract boolean isSameItem(Item otherItem); + + public ModelEnum getModelEnum() { + return modelEnum; + } +} diff --git a/src/main/java/trackr/model/item/ItemDescriptor.java b/src/main/java/trackr/model/item/ItemDescriptor.java new file mode 100644 index 00000000000..fdd592b1db2 --- /dev/null +++ b/src/main/java/trackr/model/item/ItemDescriptor.java @@ -0,0 +1,8 @@ +package trackr.model.item; + +/** + * Stores the details of an item. Each non-empty field value will replace the corresponding field value of the item. + */ +//@@author liumc-sg-reused +public interface ItemDescriptor { +} diff --git a/src/main/java/trackr/model/item/ItemList.java b/src/main/java/trackr/model/item/ItemList.java new file mode 100644 index 00000000000..119b66b2c4d --- /dev/null +++ b/src/main/java/trackr/model/item/ItemList.java @@ -0,0 +1,129 @@ +package trackr.model.item; + +import static java.util.Objects.requireNonNull; + +import java.util.Comparator; +import java.util.List; + +import javafx.collections.ObservableList; + +/** + * Wraps all data at the item-list level + * Duplicates are not allowed (by .isSameItem comparison) + */ +//@@author liumc-sg-reused +public class ItemList implements ReadOnlyItemList { + + private final UniqueItemList items; + + /* + * 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. + */ + { + items = new UniqueItemList(); + } + + public ItemList() {} + + /** + * Creates an ItemList using the Items in the {@code toBeCopied}. + */ + public ItemList(ReadOnlyItemList toBeCopied) { + this(); + resetData(toBeCopied); + } + + //// list overwrite operations + + /** + * Replaces the contents of the item list with {@code items}. + * {@code items} must not contain duplicate items. + */ + public void setItems(List items) { + this.items.setItems(items); + } + + /** + * Resets the existing data of this {@code ItemList} with {@code newData}. + */ + public void resetData(ReadOnlyItemList newData) { + requireNonNull(newData); + + setItems(newData.getItemList()); + } + + //// item-level operations + + /** + * Returns true if an item with the same identity as {@code item} exists in the item list. + */ + public boolean hasItem(T item) { + requireNonNull(item); + return items.contains(item); + } + + /** + * Adds an item to the item list. + * The item must not already exist in the item list. + */ + public void addItem(T p) { + items.add(p); + } + + /** + * Replaces the given item {@code target} in the list with {@code editedItem}. + * {@code target} must exist in the item list. + * The item identity of {@code editedItem} must not be the same as another existing item in the item list. + */ + public void setItem(T target, T editedItem) { + requireNonNull(editedItem); + + items.setItem(target, editedItem); + } + + /** + * Removes {@code key} from this {@code ItemList}. + * {@code key} must exist in the item list. + */ + public void removeItem(T key) { + items.remove(key); + } + + /** + * Sorts the items in this (@code ItemList}. + * @param comparator The comparator used to compare 2 Item objects. + */ + public void sortItems(Comparator comparator) { + requireNonNull(comparator); + items.sortItems(comparator); + } + + //// util methods + + @Override + public String toString() { + return items.asUnmodifiableObservableList().size() + " items"; + // TODO: refine later + } + + @Override + public ObservableList getItemList() { + return items.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ItemList // instanceof handles nulls + && items.equals(((ItemList) other).items)); + } + + @Override + public int hashCode() { + return items.hashCode(); + } +} diff --git a/src/main/java/trackr/model/item/ReadOnlyItemList.java b/src/main/java/trackr/model/item/ReadOnlyItemList.java new file mode 100644 index 00000000000..f3bbdf4d825 --- /dev/null +++ b/src/main/java/trackr/model/item/ReadOnlyItemList.java @@ -0,0 +1,16 @@ +package trackr.model.item; + +import javafx.collections.ObservableList; + +/** + * Unmodifiable view of an item list. + */ +//@@author liumc-sg-reused +public interface ReadOnlyItemList { + + /** + * Returns an unmodifiable view of the items list. + * This list will not contain any duplicate items. + */ + ObservableList getItemList(); +} diff --git a/src/main/java/trackr/model/item/UniqueItemList.java b/src/main/java/trackr/model/item/UniqueItemList.java new file mode 100644 index 00000000000..905d1ef030e --- /dev/null +++ b/src/main/java/trackr/model/item/UniqueItemList.java @@ -0,0 +1,160 @@ +package trackr.model.item; + +import static java.util.Objects.requireNonNull; +import static trackr.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import trackr.model.ModelEnum; +import trackr.model.item.exceptions.DuplicateItemException; +import trackr.model.item.exceptions.ItemNotFoundException; + +/** + * A list of items that enforces uniqueness between its elements and does not allow nulls. + * An item is considered unique by comparing using {@code Item#isSameItem(Item)}. As such, adding and updating of + * items uses Item#isSameItem(Item) for equality to ensure that the Item being added or updated is unique + * in terms of identity in the UniqueItemList. However, the removal of an Item uses Item#equals(Object) to + * ensure that the Item with exactly the same fields will be removed. + *

+ * Supports a minimal set of list operations. + * + * @see Item#isSameItem(Item) + */ + +//@@author liumc-sg-reused +public class UniqueItemList implements Iterable { + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + private ModelEnum itemType = ModelEnum.Item; + + protected UniqueItemList() { + } + + protected UniqueItemList(ModelEnum itemType) { + this.itemType = itemType; + } + /** + * Returns true if the list contains an equivalent item as the given argument. + */ + public boolean contains(T toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameItem); + } + + /** + * Adds an item to the list. + * The item must not already exist in the list. + */ + public void add(T toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateItemException(itemType.toString()); + } + internalList.add(toAdd); + } + + /** + * Replaces the item {@code target} in the list with {@code editedItem}. + * {@code target} must exist in the list. + * The item identity of {@code editedItem} must not be the same as another existing item in the list. + */ + public void setItem(T target, T editedItem) { + requireAllNonNull(target, editedItem); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new ItemNotFoundException(); + } + + if (!target.isSameItem(editedItem) && contains(editedItem)) { + throw new DuplicateItemException(itemType.toString()); + } + + internalList.set(index, editedItem); + } + + /** + * Removes the equivalent items from the list. + * The items must exist in the list. + */ + public void remove(T toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new ItemNotFoundException(); + } + } + + public void setItems(UniqueItemList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code items}. + * {@code items} must not contain duplicate items. + */ + public void setItems(List items) { + requireAllNonNull(items); + if (!itemsAreUnique(items)) { + throw new DuplicateItemException(itemType.toString()); + } + + internalList.setAll(items); + } + + + /** + * Sorts the items in the list according to the order imposed by the specified comparator. + * + * @param comparator the comparator used to compare the items in the list + * @throws NullPointerException if {@code comparator} is null + */ + public void sortItems(Comparator comparator) { + internalList.sort(comparator); + } + + /** + * 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 UniqueItemList // instanceof handles nulls + && internalList.equals(((UniqueItemList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code items} contains only unique items. + */ + private boolean itemsAreUnique(List items) { + for (int i = 0; i < items.size() - 1; i++) { + for (int j = i + 1; j < items.size(); j++) { + if (items.get(i).isSameItem(items.get(j))) { + return false; + } + } + } + return true; + } + +} diff --git a/src/main/java/trackr/model/item/exceptions/DuplicateItemException.java b/src/main/java/trackr/model/item/exceptions/DuplicateItemException.java new file mode 100644 index 00000000000..31ff5a65414 --- /dev/null +++ b/src/main/java/trackr/model/item/exceptions/DuplicateItemException.java @@ -0,0 +1,12 @@ +package trackr.model.item.exceptions; + +/** + * Signals that the operation will result in duplicate Items (Items are considered duplicates if they have the same + * identity). + */ +//@@author liumc-sg-reused +public class DuplicateItemException extends RuntimeException { + public DuplicateItemException(String itemName) { + super(String.format("Operation would result in duplicate %ss", itemName)); + } +} diff --git a/src/main/java/trackr/model/item/exceptions/ItemNotFoundException.java b/src/main/java/trackr/model/item/exceptions/ItemNotFoundException.java new file mode 100644 index 00000000000..cd4dbcc981a --- /dev/null +++ b/src/main/java/trackr/model/item/exceptions/ItemNotFoundException.java @@ -0,0 +1,8 @@ +package trackr.model.item.exceptions; + +/** + * Signals that the operation is unable to find the specified item. + */ +//@@author liumc-sg-reused +public class ItemNotFoundException extends RuntimeException { +} diff --git a/src/main/java/trackr/model/menu/ItemCost.java b/src/main/java/trackr/model/menu/ItemCost.java new file mode 100644 index 00000000000..9e29fd48210 --- /dev/null +++ b/src/main/java/trackr/model/menu/ItemCost.java @@ -0,0 +1,21 @@ +package trackr.model.menu; + +/** + * Represents a Item's cost in the Item list. + * Guarantees: immutable; is valid as declared in {@link #isValidPrice(String)} + */ +//@@author changgittyhub-reused +public class ItemCost extends ItemPrice { + + /** + * Constructs an ItemCost Object + */ + public ItemCost(String value) { + super(value); + } + + @Override + public String toString() { + return "Cost: $" + this.getFormattedValue(); + } +} diff --git a/src/main/java/trackr/model/menu/ItemName.java b/src/main/java/trackr/model/menu/ItemName.java new file mode 100644 index 00000000000..fcae6306470 --- /dev/null +++ b/src/main/java/trackr/model/menu/ItemName.java @@ -0,0 +1,26 @@ +package trackr.model.menu; + +import trackr.model.commons.Name; + +/** + * Represents a Item's name in the Item list. + * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} + */ +//@@author changgittyhub-reused +public class ItemName extends Name { + + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + public static final String MESSAGE_CONSTRAINTS = + "Item names should only contain alphanumeric characters and spaces, and it should not be blank"; + //@@author + + /** + * Constructs an {@code ItemName}. + * + * @param itemName A valid Item name. + */ + public ItemName(String itemName) { + super(itemName, "Item"); + } +} diff --git a/src/main/java/trackr/model/menu/ItemNameContainsKeywordsPredicate.java b/src/main/java/trackr/model/menu/ItemNameContainsKeywordsPredicate.java new file mode 100644 index 00000000000..0c233190f28 --- /dev/null +++ b/src/main/java/trackr/model/menu/ItemNameContainsKeywordsPredicate.java @@ -0,0 +1,39 @@ +package trackr.model.menu; + +import java.util.List; +import java.util.function.Predicate; + +import trackr.commons.util.StringUtil; +import trackr.model.item.Item; + +/** + * Tests that a {@code MenuItem}'s {@code ItemName} matches any of the keywords given. + */ +//@@author changgittyhub-reused +public class ItemNameContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public ItemNameContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Item item) { + if (!(item instanceof MenuItem)) { + return false; + } + + MenuItem menuItem = (MenuItem) item; + + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(menuItem.getItemName().getName(), keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ItemNameContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((ItemNameContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/trackr/model/menu/ItemPrice.java b/src/main/java/trackr/model/menu/ItemPrice.java new file mode 100644 index 00000000000..109c7f0a7fd --- /dev/null +++ b/src/main/java/trackr/model/menu/ItemPrice.java @@ -0,0 +1,99 @@ +package trackr.model.menu; + +import static java.util.Objects.requireNonNull; +import static trackr.commons.util.AppUtil.checkArgument; + +import java.text.DecimalFormat; + +/** + * ItemPrice represents any currency that may be associated. + */ +//@@author changgittyhub-reused +public class ItemPrice { + public static final String MESSAGE_CONSTRAINTS = + "Price should only contain positive numbers, and it should be at most 2 decimal place"; + private static final String VALIDATION_REGEX = "^[0-9]+(\\.[0-9]{0,2})?$"; + private static final DecimalFormat DF = new DecimalFormat("0.00"); + private final Double value; + private final String formattedValue; + + /** + * Constructs an ItemPrice object with the given price value as a String. + * + * @param value The price value as a String. + * @throws NullPointerException If value is null. + * @throws IllegalArgumentException If value is not a valid price value. + */ + public ItemPrice(String value) { + requireNonNull(value); + checkArgument(isValidPrice(value), MESSAGE_CONSTRAINTS); + this.value = Double.parseDouble(value); + this.formattedValue = DF.format(this.value); + } + + /** + * Constructs an ItemPrice object with the given price value as a Double. + * + * @param itemPrice The price value as a Double. + * @throws NullPointerException If itemPrice is null. + */ + public ItemPrice(Double itemPrice) { + requireNonNull(itemPrice); + this.value = itemPrice; + this.formattedValue = DF.format(this.value); + } + + /** + * Returns true if a given string matches the validation regex for price. + * + * @param test The string to test for validity as a price value. + * @return True if test is a valid price value, false otherwise. + */ + public static boolean isValidPrice(String test) { + return test.matches(VALIDATION_REGEX); + } + + /** + * Returns the price value as a Double. + * + * @return the price value as a Double + */ + public Double getValue() { + return value; + } + + /** + * Returns the formatted price value as a String. + * + * @return the formatted price value as a String. + */ + public String getFormattedValue() { + return formattedValue; + } + + /** + * Returns the formatted price value of the item as a JSON string for storage purposes. + * + * @return The formatted price value as a JSON string. + */ + public String toJsonString() { + return formattedValue; + } + + @Override + public String toString() { + return formattedValue; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ItemPrice // instanceof handles nulls + && value.equals(((ItemPrice) other).getValue())); // state check + } + + @Override + public int hashCode() { + return Double.hashCode(value); + } +} diff --git a/src/main/java/trackr/model/menu/ItemProfit.java b/src/main/java/trackr/model/menu/ItemProfit.java new file mode 100644 index 00000000000..d012e5f85f9 --- /dev/null +++ b/src/main/java/trackr/model/menu/ItemProfit.java @@ -0,0 +1,99 @@ +package trackr.model.menu; + +import static java.util.Objects.requireNonNull; +import static trackr.commons.util.AppUtil.checkArgument; +import static trackr.commons.util.CollectionUtil.requireAllNonNull; + +import java.text.DecimalFormat; + +/** + * Represents an Item's profit in the Item list. + * Guarantees: immutable; is valid as declared in {@link #isValidProfit(String)} + */ +//@@author changgittyhub-reused +public class ItemProfit { + public static final String MESSAGE_CONSTRAINTS = + "Profit should only contain numbers, and it should be at most 2 decimal place"; + private static final String VALIDATION_REGEX = "^-?[0-9]+(\\.[0-9]{1,2})?$"; + private static final DecimalFormat DF = new DecimalFormat("0.00"); + private final Double value; + private final String formattedValue; + + + /** + * Constructs an ItemProfit object based on selling price and selling cost of an item. + * + * @param itemSellingPrice The selling price of the item. + * @param itemSellingCost The selling cost of the item. + * @throws NullPointerException if any of the arguments are null. + * @throws IllegalArgumentException if the calculated profit is invalid. + */ + public ItemProfit(ItemSellingPrice itemSellingPrice, ItemCost itemSellingCost) { + requireAllNonNull(itemSellingPrice, itemSellingCost); + this.value = itemSellingPrice.getValue() - itemSellingCost.getValue(); + this.formattedValue = DF.format(this.value); + checkArgument(isValidProfit(formattedValue), MESSAGE_CONSTRAINTS); + } + + /** + * Constructs an ItemProfit object with the given profit value. + * + * @param itemProfit The profit value of the item of any numerical format + * @throws NullPointerException if the argument is null. + */ + public ItemProfit(Double itemProfit) { + requireNonNull(itemProfit); + this.value = itemProfit; + this.formattedValue = DF.format(this.value); + } + + /** + * Returns true if a given string matches the validation regex for profit. + * + * @param test The string to test for validity as a profit value + * @return True if test is a valid profit value, false otherwise + */ + public static boolean isValidProfit(String test) { + return test.matches(VALIDATION_REGEX); + } + + /** + * Returns the profit value of the item. + * + * @return The profit value. + */ + public Double getValue() { + return value; + } + + /** + * Returns the formatted profit value of the item as a JSON string for storage purposes. + * . + * @return The formatted profit value as a JSON string. + */ + public String toJsonString() { + return formattedValue; + } + + /** + * Returns a string representation of the ItemProfit object. + * + * @return A string representation of the object. + */ + @Override + public String toString() { + return "Profit: $" + formattedValue; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ItemProfit // instanceof handles nulls + && value.equals(((ItemProfit) other).getValue())); // state check + } + + @Override + public int hashCode() { + return Double.hashCode(value); + } +} diff --git a/src/main/java/trackr/model/menu/ItemSellingPrice.java b/src/main/java/trackr/model/menu/ItemSellingPrice.java new file mode 100644 index 00000000000..cad2c6b74e4 --- /dev/null +++ b/src/main/java/trackr/model/menu/ItemSellingPrice.java @@ -0,0 +1,24 @@ +package trackr.model.menu; + +/** + * Represents a Item's price in the Item list. + * Guarantees: immutable; is valid as declared in {@link #isValidPrice(String)} + */ +//@@author changgittyhub-reused +public class ItemSellingPrice extends ItemPrice { + + /** + * Constructs a {@code ItemSellingPrice}. + */ + public ItemSellingPrice(String value) { + super(value); + } + + public ItemSellingPrice(Double value) { + super(value); + } + @Override + public String toString() { + return "Selling Price: $" + this.getFormattedValue(); + } +} diff --git a/src/main/java/trackr/model/menu/MenuItem.java b/src/main/java/trackr/model/menu/MenuItem.java new file mode 100644 index 00000000000..9171eadcdfb --- /dev/null +++ b/src/main/java/trackr/model/menu/MenuItem.java @@ -0,0 +1,111 @@ +package trackr.model.menu; + +import static trackr.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Objects; + +import trackr.model.ModelEnum; +import trackr.model.item.Item; + +/** + * Represents a Menu in the menu list. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +//@@author changgittyhub-reused +public class MenuItem extends Item { + + // Data fields + private final ItemName itemName; + private final ItemSellingPrice itemPrice; + private final ItemCost itemCost; + private final ItemProfit itemProfit; + + /** + * Every field must be present and not null. + */ + public MenuItem(ItemName itemName, ItemSellingPrice itemPrice, ItemCost itemCost) { + super(ModelEnum.Item); + requireAllNonNull(itemName, itemPrice, itemCost); + this.itemName = itemName; + this.itemPrice = itemPrice; + this.itemCost = itemCost; + this.itemProfit = new ItemProfit(itemPrice, itemCost); + } + + public ItemName getItemName() { + return itemName; + } + + public ItemSellingPrice getItemPrice() { + return itemPrice; + } + + public ItemCost getItemCost() { + return itemCost; + } + + public ItemProfit getItemProfit() { + return itemProfit; + } + + /** + * Returns true if both Items have the same name. + * This defines a weaker notion of equality between two Items. + */ + @Override + public boolean isSameItem(Item otherItem) { + if (otherItem == this) { + return true; + } + + if (!(otherItem instanceof MenuItem)) { + return false; + } + + MenuItem otherMenuItem = (MenuItem) otherItem; + + return otherItem != null + && otherMenuItem.getItemName().equals(getItemName()); + } + + /** + * Returns true if both Items have the same name, cost and price. + * This defines a stronger notion of equality between two Items. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof MenuItem)) { + return false; + } + + MenuItem otherMenuItem = (MenuItem) other; + + return otherMenuItem != null + && otherMenuItem.getItemName().equals(getItemName()) + && otherMenuItem.getItemPrice().equals(getItemPrice()) + && otherMenuItem.getItemCost().equals(getItemCost()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(itemName, itemPrice, itemCost); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getItemName()) + .append("; ") + .append(getItemPrice()) + .append("; ") + .append(getItemCost()) + .append("; ") + .append(getItemProfit()); + return builder.toString(); + } +} diff --git a/src/main/java/trackr/model/menu/MenuItemDescriptor.java b/src/main/java/trackr/model/menu/MenuItemDescriptor.java new file mode 100644 index 00000000000..54898c74f1b --- /dev/null +++ b/src/main/java/trackr/model/menu/MenuItemDescriptor.java @@ -0,0 +1,89 @@ +package trackr.model.menu; + +import java.util.Optional; + +import trackr.commons.util.CollectionUtil; +import trackr.model.item.ItemDescriptor; + +/** + * Stores the details of a task. + * Each non-empty field value will replace the corresponding field value of the task. + */ +//@@author changgittyhub-reused +public class MenuItemDescriptor implements ItemDescriptor { + private ItemName itemName; + private ItemSellingPrice itemPrice; + private ItemCost itemCost; + private ItemProfit itemProfit; + + public MenuItemDescriptor() {} + + /** + * Copy constructor. + */ + public MenuItemDescriptor(MenuItemDescriptor toCopy) { + setItemName(toCopy.itemName); + setItemPrice(toCopy.itemPrice); + setItemCost(toCopy.itemCost); + setItemProfit(toCopy.itemProfit); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldNonNull() { + return CollectionUtil.isAnyNonNull(itemName, itemPrice, itemCost, itemProfit); + } + + public void setItemName(ItemName itemName) { + this.itemName = itemName; + } + + public Optional getItemName() { + return Optional.ofNullable(itemName); + } + + public void setItemPrice(ItemSellingPrice itemPrice) { + this.itemPrice = itemPrice; + } + + public Optional getItemPrice() { + return Optional.ofNullable(itemPrice); + } + + public void setItemCost(ItemCost itemCost) { + this.itemCost = itemCost; + } + + public Optional getItemCost() { + return Optional.ofNullable(itemCost); + } + + public void setItemProfit(ItemProfit itemProfit) { + this.itemProfit = itemProfit; + } + + public Optional getItemProfit() { + return Optional.ofNullable(itemProfit); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof MenuItemDescriptor)) { + return false; + } + + // state check + MenuItemDescriptor menuItemDescriptor = (MenuItemDescriptor) other; + + return getItemName().equals(menuItemDescriptor.getItemName()) + && getItemPrice().equals(menuItemDescriptor.getItemPrice()) + && getItemCost().equals(menuItemDescriptor.getItemCost()); + } +} diff --git a/src/main/java/trackr/model/menu/UniqueMenu.java b/src/main/java/trackr/model/menu/UniqueMenu.java new file mode 100644 index 00000000000..f8627a2e51c --- /dev/null +++ b/src/main/java/trackr/model/menu/UniqueMenu.java @@ -0,0 +1,26 @@ +package trackr.model.menu; + +import trackr.model.ModelEnum; +import trackr.model.item.Item; +import trackr.model.item.UniqueItemList; + +/** + * A list of MenuItems that enforces uniqueness between its elements and does not allow nulls. + * A MenuItem is considered unique by comparing using {@code MenuItem#isSameItem(Item)}. + * As such, adding and updating of MenuItems uses MenuItem#isSameItem(Item) for equality + * to ensure that the MenuItem being added or updated is unique + * in terms of identity in the UniqueMenuItemList. + * However, the removal of a MenuItem uses MenuItem#equals(Object) to + * ensure that the MenuItem with exactly the same fields will be removed. + *

+ * Supports a minimal set of list operations. + * + * @see MenuItem#isSameItem(Item) + */ +//@@author changgittyhub-reused +public class UniqueMenu extends UniqueItemList { + + public UniqueMenu() { + super(ModelEnum.MENUITEM); + } +} diff --git a/src/main/java/trackr/model/order/Order.java b/src/main/java/trackr/model/order/Order.java new file mode 100644 index 00000000000..7531c71bd97 --- /dev/null +++ b/src/main/java/trackr/model/order/Order.java @@ -0,0 +1,281 @@ +package trackr.model.order; + +import static trackr.commons.util.CollectionUtil.requireAllNonNull; + +import java.time.LocalDateTime; +import java.util.Objects; + +import trackr.model.ModelEnum; +import trackr.model.item.Item; +import trackr.model.menu.ItemCost; +import trackr.model.menu.ItemName; +import trackr.model.menu.ItemPrice; +import trackr.model.menu.ItemProfit; +import trackr.model.menu.ItemSellingPrice; +import trackr.model.menu.MenuItem; +import trackr.model.person.Customer; + +/** + * Represents an Order in the order list. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Order extends Item { + + //@@author chongweiguan-reused + private static final MenuItem INVALID_MENU_ITEM = + new MenuItem(new ItemName("INVALID"), new ItemSellingPrice("99.99"), new ItemCost("99.99")); + + // Data fields + private final OrderName orderName; + private final OrderDeadline orderDeadline; + private final OrderStatus orderStatus; + private final OrderQuantity orderQuantity; + private final LocalDateTime timeAdded; + + // Customer + private final Customer customer; + + // MenuItem + private final MenuItem orderItem; + + //@@author arkarsg + /** + * Every field must be present and not null + */ + public Order(MenuItem orderItem, OrderDeadline orderDeadline, OrderStatus orderStatus, + OrderQuantity orderQuantity, Customer customer) { + this(orderItem, new OrderName(orderItem.getItemName().toString()), orderDeadline, orderStatus, orderQuantity, + customer, LocalDateTime.now()); + } + //@@author + + /** + * Every field must be present and not null + */ + public Order(OrderName orderName, OrderDeadline orderDeadline, OrderStatus orderStatus, + OrderQuantity orderQuantity, Customer customer) { + this(INVALID_MENU_ITEM, orderName, orderDeadline, orderStatus, orderQuantity, customer, LocalDateTime.now()); + } + + /** + * Every field must be present and not null + */ + public Order(OrderName orderName, OrderDeadline orderDeadline, OrderStatus orderStatus, + OrderQuantity orderQuantity, Customer customer, LocalDateTime timeAdded) { + this(INVALID_MENU_ITEM, orderName, orderDeadline, orderStatus, orderQuantity, customer, timeAdded); + } + + + /** + * Every field must be present and not null + */ + public Order(MenuItem orderItem, OrderDeadline orderDeadline, OrderStatus orderStatus, + OrderQuantity orderQuantity, Customer customer, LocalDateTime timeAdded) { + this(orderItem, new OrderName(orderItem.getItemName().toString()), orderDeadline, orderStatus, orderQuantity, + customer, timeAdded); + } + + /** + * Every field must be present and not null + */ + public Order(MenuItem orderItem, OrderName orderName, OrderDeadline orderDeadline, OrderStatus orderStatus, + OrderQuantity orderQuantity, Customer customer, LocalDateTime timeAdded) { + super(ModelEnum.ORDER); + requireAllNonNull(orderItem, orderName, orderDeadline, orderStatus, orderQuantity, customer, timeAdded); + this.orderItem = orderItem; + this.orderName = orderName; + this.orderDeadline = orderDeadline; + this.orderStatus = orderStatus; + this.orderQuantity = orderQuantity; + this.customer = customer; + this.timeAdded = timeAdded; + } + + //@@author arkarsg + public MenuItem getOrderItem() { + return orderItem; + } + + public OrderName getOrderName() { + return orderName; + } + + public OrderDeadline getOrderDeadline() { + return orderDeadline; + } + + public OrderStatus getOrderStatus() { + return orderStatus; + } + + public OrderQuantity getOrderQuantity() { + return orderQuantity; + } + + public Customer getCustomer() { + return customer; + } + //@@author + + public LocalDateTime getTimeAdded() { + return timeAdded; + } + + //@@author arkarsg + public ItemProfit getTotalProfit() { + return OrderUtil.getTotalProfit(orderQuantity, orderItem); + } + //@@author + + //@@author arkarsg + public ItemPrice getTotalCost() { + return OrderUtil.getTotalCost(orderQuantity, orderItem); + } + //@@author + + //@@author arkarsg + public ItemPrice getTotalRevenue() { + return OrderUtil.getTotalRevenue(orderQuantity, orderItem); + } + //@@author + + /** + * Compares 2 tasks using their time added. + * + * @param otherOrder The order to compare with. + * @return -1 if this order was added earlier than the other order. + * Returns 1 if this order was added later than the other order + * Returns 0 if both orders were added at the same time. + */ + public int compareTimeAdded(Order otherOrder) { + return timeAdded.compareTo(otherOrder.timeAdded); + } + + /** + * Compares 2 orders using their names (ignoring case). + * + * @param otherOrder The order to compare with. + * @return 1 if this name is lexicographically larger (ignoring case) than the other name, + * -1 if this name is lexicographically smaller (ignoring case) than the other name, + * 0 if both names are lexicographically (ignoring case) equal. + */ + public int compareName(Order otherOrder) { + return orderName.compare(otherOrder.getOrderName()); + } + + /** + * Compares 2 orders using their deadlines. + * + * @param otherOrder The order to compare with. + * @return -1 if this order has an earlier deadline than the other order. + * Returns 1 if this order has a later deadline than the other order + * Returns 0 if both orders have the same deadlines. + */ + public int compareDeadline(Order otherOrder) { + return orderDeadline.compare(otherOrder.getOrderDeadline()); + } + + /** + * Compares 2 orders using their statuses. + * + * @param otherOrder The order to compare with. + * @return 1 if this order's status has lower a sorting priority than the other order's status, + * returns -1 if this order's status has higher a sorting priority than the other order's status, + * returns 0 if both tasks have the same statuses. + * (The sorting priority is Not Delivered > In Progress > Delivered) + */ + public int compareStatus(Order otherOrder) { + return orderStatus.compare(otherOrder.getOrderStatus()); + } + + /** + * Compares 2 orders using their statuses and deadlines. + * + * @param otherOrder The order to compare with. + * @return -1 if this order's status has higher a sorting priority than the other order's status, + * or if both have the same status but this order has an earlier deadline. + * Returns 1 if this order's status has lower a sorting priority than the other order's status, + * or if both have the same status but this order has a later deadline. + * Returns 0 if both tasks have the same statuses and deadlines. + * (The sorting priority for order status is Not Delivered > In Progress > Delivered) + */ + public int compareStatusAndDeadline(Order otherOrder) { + int compareStatusResult = orderStatus.compare(otherOrder.getOrderStatus()); + + if (compareStatusResult != 0) { + return compareStatusResult; + } + //both order statuses are equal so compare deadline + return orderDeadline.compare(otherOrder.getOrderDeadline()); + } + + //@@author chongweiguan-reused + /** + * Returns true if both orders have the same name, deadline, quantity and customer + * This defines a weaker notion of equality between two orders. + */ + public boolean isSameItem(Item otherItem) { + if (otherItem == this) { + return true; + } + + if (!(otherItem instanceof Order)) { + return false; + } + + Order otherOrder = (Order) otherItem; + + return otherOrder != null + && otherOrder.getCustomer().equals(this.getCustomer()) + && otherOrder.getOrderName().equals(this.getOrderName()) + && otherOrder.getOrderItem().equals(this.getOrderItem()) + && otherOrder.getOrderDeadline().equals(this.getOrderDeadline()) + && otherOrder.getOrderQuantity().equals(this.getOrderQuantity()); + } + + /** + * Returns true if both orders have the same name, deadline, quantity, customer and status. + * This defines a stronger notion of equality between two orders. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Order)) { + return false; + } + + Order otherOrder = (Order) other; + + return otherOrder != null + && otherOrder.getCustomer().equals(this.getCustomer()) + && otherOrder.getOrderName().equals(this.getOrderName()) + && otherOrder.getOrderDeadline().equals(this.getOrderDeadline()) + && otherOrder.getOrderStatus().equals(this.getOrderStatus()) + && otherOrder.getOrderQuantity().equals(this.getOrderQuantity()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(customer, orderName, orderDeadline, orderStatus, orderQuantity); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getOrderName()) + .append("; ") + .append(getOrderQuantity()) + .append("; Deadline: ") + .append(getOrderDeadline()) + .append("; Status: ") + .append(getOrderStatus()) + .append("; Customer: ") + .append(getCustomer()); + return builder.toString(); + } + //@@author +} diff --git a/src/main/java/trackr/model/order/OrderContainsKeywordsPredicate.java b/src/main/java/trackr/model/order/OrderContainsKeywordsPredicate.java new file mode 100644 index 00000000000..34adbc53f14 --- /dev/null +++ b/src/main/java/trackr/model/order/OrderContainsKeywordsPredicate.java @@ -0,0 +1,153 @@ +package trackr.model.order; + +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +import trackr.commons.util.StringUtil; +import trackr.model.item.Item; + +//@@author chongweiguan-reused +/** + * Tests that a {@code Order}'s {@code OrderName} matches any of the keywords given. + */ +public class OrderContainsKeywordsPredicate extends OrderDescriptor implements Predicate { + private List orderNameKeywords; + private List customerNameKeywords; + + + public OrderContainsKeywordsPredicate() { + super(); + } + + /** + * Constructs a new {@code OrderContainsKeywordsPredicate} object + * with the same keywords of order details as the + * {@code OrderContainsKeywordsPredicate} object specified. + * @param toCopy The {@code OrderContainsKeywordsPredicate} object + * to copy the order keywords from. + */ + public OrderContainsKeywordsPredicate(OrderContainsKeywordsPredicate toCopy) { + setOrderNameKeywords(toCopy.orderNameKeywords); + setOrderDeadline(toCopy.getOrderDeadline().isPresent() ? toCopy.getOrderDeadline().get() : null); + setOrderStatus(toCopy.getOrderStatus().isPresent() ? toCopy.getOrderStatus().get() : null); + setOrderQuantity(toCopy.getOrderQuantity().isPresent() ? toCopy.getOrderQuantity().get() : null); + setCustomerAddress(toCopy.getCustomerAddress().isPresent() ? toCopy.getCustomerAddress().get() : null); + setCustomerNameKeywords(toCopy.customerNameKeywords); + setCustomerPhone(toCopy.getCustomerPhone().isPresent() ? toCopy.getCustomerPhone().get() : null); + } + + public void setOrderNameKeywords(List orderNameKeywords) { + this.orderNameKeywords = orderNameKeywords; + } + + public Optional> getOrderNameKeywords() { + return Optional.ofNullable(orderNameKeywords); + } + + public void setCustomerNameKeywords(List customerNameKeywords) { + this.customerNameKeywords = customerNameKeywords; + } + + public Optional> getCustomerNameKeywords() { + return Optional.ofNullable(customerNameKeywords); + } + + /** + * Returns true if any of the fields in the {@code Order} object are present or not. + * + * @return true if any of the fields in the {@code Order} object are present or not. + */ + public boolean isAnyFieldPresent() { + return isAnyFieldNonNull() || orderNameKeywords != null || customerNameKeywords != null; + } + + @Override + public boolean test(Item item) { + if (!(item instanceof Order)) { + return false; + } + + Order order = (Order) item; + + + boolean isOrderNameMatch; + boolean isOrderDeadlineMatch; + boolean isOrderQuantityMatch; + boolean isOrderStatusMatch; + boolean isCustomerNameMatch; + boolean isCustomerAddressMatch; + boolean isCustomerPhoneMatch; + + if (orderNameKeywords != null) { + return orderNameKeywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(order.getOrderName().getName(), keyword)); + } else { + isOrderNameMatch = true; + } + + if (getOrderDeadline().isPresent()) { + isOrderDeadlineMatch = getOrderDeadline().get().equals(order.getOrderDeadline()); + } else { + isOrderDeadlineMatch = true; + } + + if (getOrderQuantity().isPresent()) { + isOrderQuantityMatch = getOrderQuantity().get().equals(order.getOrderQuantity()); + } else { + isOrderQuantityMatch = true; + } + + if (getOrderStatus().isPresent()) { + isOrderStatusMatch = getOrderStatus().get().equals(order.getOrderStatus()); + } else { + isOrderStatusMatch = true; + } + + if (customerNameKeywords != null) { + return customerNameKeywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase( + order.getCustomer().getCustomerName().getName(), keyword)); + } else { + isCustomerNameMatch = true; + } + + if (getCustomerPhone().isPresent()) { + isCustomerPhoneMatch = getCustomerPhone().get().equals(order.getCustomer().getCustomerPhone()); + } else { + isCustomerPhoneMatch = true; + } + + if (getCustomerAddress().isPresent()) { + isCustomerAddressMatch = getCustomerAddress().get().equals(order.getCustomer().getCustomerAddress()); + } else { + isCustomerAddressMatch = true; + } + + return isOrderNameMatch && isOrderDeadlineMatch && isOrderQuantityMatch && isOrderStatusMatch + && isCustomerAddressMatch && isCustomerNameMatch && isCustomerPhoneMatch; + + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof OrderContainsKeywordsPredicate)) { + return false; + } + + OrderContainsKeywordsPredicate predicate = (OrderContainsKeywordsPredicate) other; + + return getCustomerAddress().equals(predicate.getCustomerAddress()) + && getCustomerNameKeywords().equals(predicate.getCustomerNameKeywords()) + && getCustomerPhone().equals(predicate.getCustomerPhone()) + && getOrderNameKeywords().equals(predicate.getOrderNameKeywords()) + && getOrderQuantity().equals(predicate.getOrderQuantity()) + && getOrderStatus().equals(predicate.getOrderStatus()) + && getOrderDeadline().equals(predicate.getOrderDeadline()); + } + //@@author +} diff --git a/src/main/java/trackr/model/order/OrderDeadline.java b/src/main/java/trackr/model/order/OrderDeadline.java new file mode 100644 index 00000000000..36b2ee1a370 --- /dev/null +++ b/src/main/java/trackr/model/order/OrderDeadline.java @@ -0,0 +1,26 @@ +package trackr.model.order; + +import trackr.model.commons.Deadline; + +/** + * Represents an Order's deadline in the order list. + * Guarantees: immutable; is valid as declared in {@link #isValidDeadline(String)} + */ +public class OrderDeadline extends Deadline { + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + public static final String MESSAGE_CONSTRAINTS = + "Order deadline should only contain numeric values " + + "in the format \"DD/MM/YYYY\" and it should not be blank."; + //@@author + + + /** + * Constructs a {@code OrderDeadline}. + * + * @param deadline A valid deadline. + */ + public OrderDeadline(String deadline) { + super(deadline, "Order"); + } +} diff --git a/src/main/java/trackr/model/order/OrderDescriptor.java b/src/main/java/trackr/model/order/OrderDescriptor.java new file mode 100644 index 00000000000..ec9e740a44b --- /dev/null +++ b/src/main/java/trackr/model/order/OrderDescriptor.java @@ -0,0 +1,141 @@ +package trackr.model.order; + +import java.util.Optional; + +import trackr.commons.util.CollectionUtil; +import trackr.model.item.ItemDescriptor; +import trackr.model.menu.MenuItem; +import trackr.model.person.PersonAddress; +import trackr.model.person.PersonName; +import trackr.model.person.PersonPhone; + +//@@author chongweiguan-reused +/** + * Stores the details of an order. Each non-empty field value will replace the corresponding field value of the order. + */ +//@@author chongweiguan-reused +public class OrderDescriptor implements ItemDescriptor { + private OrderName orderName; + private OrderDeadline orderDeadline; + private OrderQuantity orderQuantity; + private OrderStatus orderStatus; + private PersonName customerName; + private PersonPhone customerPhone; + private PersonAddress customerAddress; + private MenuItem orderItem; + + public OrderDescriptor() {} + + /** + * Copy constructor. + */ + public OrderDescriptor(OrderDescriptor toCopy) { + setOrderName(toCopy.orderName); + setOrderDeadline(toCopy.orderDeadline); + setOrderQuantity(toCopy.orderQuantity); + setOrderStatus(toCopy.orderStatus); + setCustomerName(toCopy.customerName); + setCustomerPhone(toCopy.customerPhone); + setCustomerAddress(toCopy.customerAddress); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldNonNull() { + return CollectionUtil.isAnyNonNull(orderDeadline, orderName, orderQuantity, + orderStatus, customerAddress, customerName, customerPhone); + } + + public void setCustomerAddress(PersonAddress customerAddress) { + this.customerAddress = customerAddress; + } + + public Optional getCustomerAddress() { + return Optional.ofNullable(customerAddress); + } + + public void setCustomerName(PersonName customerName) { + this.customerName = customerName; + } + + public Optional getCustomerName() { + return Optional.ofNullable(customerName); + } + + public void setCustomerPhone(PersonPhone customerPhone) { + this.customerPhone = customerPhone; + } + + public Optional getCustomerPhone() { + return Optional.ofNullable(customerPhone); + } + + public void setOrderDeadline(OrderDeadline orderDeadline) { + this.orderDeadline = orderDeadline; + } + + public Optional getOrderDeadline() { + return Optional.ofNullable(orderDeadline); + } + + public void setOrderName(OrderName orderName) { + this.orderName = orderName; + } + + public Optional getOrderName() { + return Optional.ofNullable(orderName); + } + + public void setOrderQuantity(OrderQuantity orderQuantity) { + this.orderQuantity = orderQuantity; + } + + public Optional getOrderQuantity() { + return Optional.ofNullable(orderQuantity); + } + + public void setOrderStatus(OrderStatus orderStatus) { + this.orderStatus = orderStatus; + } + + public Optional getOrderStatus() { + return Optional.ofNullable(orderStatus); + } + + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + public void setOrderItem(MenuItem orderItem) { + this.orderItem = orderItem; + } + + public Optional getOrderItem() { + return Optional.ofNullable(orderItem); + } + //@@author + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof OrderDescriptor)) { + return false; + } + + // state check + OrderDescriptor orderDescriptor = (OrderDescriptor) other; + + return getOrderName().equals(orderDescriptor.getOrderName()) + && getOrderDeadline().equals(orderDescriptor.getOrderDeadline()) + && getOrderStatus().equals(orderDescriptor.getOrderStatus()) + && getOrderQuantity().equals(orderDescriptor.getOrderQuantity()) + && getCustomerAddress().equals(orderDescriptor.getCustomerAddress()) + && getCustomerPhone().equals(orderDescriptor.getCustomerPhone()) + && getCustomerName().equals(orderDescriptor.getCustomerName()); + } + //@@author +} diff --git a/src/main/java/trackr/model/order/OrderName.java b/src/main/java/trackr/model/order/OrderName.java new file mode 100644 index 00000000000..f98a22601a5 --- /dev/null +++ b/src/main/java/trackr/model/order/OrderName.java @@ -0,0 +1,25 @@ +package trackr.model.order; + +import trackr.model.commons.Name; + +/** + * Represents an Order's name in the order list. + * Guaruntees: immutable; is valid as declared in {@link #isValidName(String)} + */ +public class OrderName extends Name { + + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + public static final String MESSAGE_CONSTRAINTS = + "Order names should only contain alphanumeric characters and spaces, and it should not be blank"; + //@@author + + /** + * Constructs a {@code OrderName}. + * + * @param orderName A valid order name. + */ + public OrderName(String orderName) { + super(orderName, "Order"); + } +} diff --git a/src/main/java/trackr/model/order/OrderQuantity.java b/src/main/java/trackr/model/order/OrderQuantity.java new file mode 100644 index 00000000000..c7584e2d243 --- /dev/null +++ b/src/main/java/trackr/model/order/OrderQuantity.java @@ -0,0 +1,71 @@ +package trackr.model.order; + +import static java.util.Objects.requireNonNull; +import static trackr.commons.util.AppUtil.checkArgument; + +/** + * Represents the Order's Quantity + * Guaruntees: immutable; is valid as declared in {@link #isValidQuantity(String)} + */ +public class OrderQuantity { + public static final String MESSAGE_CONSTRAINTS = + "Quantity should only contain numbers, and it should be at most 3 digits long"; + public static final String VALIDATION_REGEX = "\\d{1,3}"; + public final String value; + + //@@author chongweiguan-reused + /** + * Constructs an Order Quantity Object. + */ + public OrderQuantity(String quantity) { + requireNonNull(quantity); + checkArgument(isValidQuantity(quantity), MESSAGE_CONSTRAINTS); + value = quantity; + } + + /** + * Returns true if a given string matches the validation regex for quantity. + * + * @param test the string to test for validity as a quantity value + * @return true if test is a valid quantity value, false otherwise + */ + public static boolean isValidQuantity(String test) { + return test.matches(VALIDATION_REGEX); + } + + /** + * Returns an order quantity value in the form of an int from a string value. + * + * @return {@code int} form of value string. + */ + public int getOrderQuantity() { + return Integer.parseInt(value); + } + + /** + * Returns an order quantity value in the form of a string. + * + * @return value in the form of a string. + */ + public String getValue() { + return value; + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof OrderQuantity // instanceof handles nulls + && value.equals(((OrderQuantity) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + //@@author +} diff --git a/src/main/java/trackr/model/order/OrderStatus.java b/src/main/java/trackr/model/order/OrderStatus.java new file mode 100644 index 00000000000..2b93f61a6f0 --- /dev/null +++ b/src/main/java/trackr/model/order/OrderStatus.java @@ -0,0 +1,84 @@ +package trackr.model.order; + +import java.util.HashMap; +import java.util.Map; + +import trackr.model.commons.Status; + +/** + * Represents an Order's status in the order list. + * Guaruntees: immutable; is valid as declared in {@link #isValidStatus(String, HashMap)} + */ +public class OrderStatus extends Status { + + public static final HashMap STATUSES; + public static final String STATUS_MESSAGE; + public static final String MESSAGE_CONSTRAINTS; + + static { + STATUSES = new HashMap<>(); + STATUSES.put("N", "Not Delivered"); + STATUSES.put("I", "In Progress"); + STATUSES.put("D", "Delivered"); + + StringBuilder statusMessageBuilder = new StringBuilder(); + for (Map.Entry entry : STATUSES.entrySet()) { + statusMessageBuilder.append(String.format(" `%s` or `%s` for %s,", entry.getKey(), + entry.getKey().toLowerCase(), entry.getValue())); + } + STATUS_MESSAGE = + statusMessageBuilder.deleteCharAt(statusMessageBuilder.length() - 1).append(".").toString(); + MESSAGE_CONSTRAINTS = String.format(Status.MESSAGE_CONSTRAINTS_FORMAT, "Order", STATUS_MESSAGE); + } + + /** + * Constructs a {@code OrderStatus} with a given status input. + * + * @param status A valid status. + */ + public OrderStatus(String status) { + super(status, "Order", STATUSES); + } + + /** + * Constructs a {@code TaskStatus} (with no status input, default case). + */ + public OrderStatus() { + this("N"); + } + + /** + * Compares this status to a given status. + * + * @param other The other status to compare this status to. + * @return -1, 1 or 0 according to the status sorting criteria. + * OrderStatus have descending sorting priority in the order: N > I > D. + * Returns -1 this order status has a higher sorting priority, + * 1 if this order status has a lower sorting priority, + * 0 if both statuses are the same. + */ + @Override + public int compare(Status other) { + if (toJsonString().equalsIgnoreCase(other.toJsonString())) { + //both statuses are the same + return 0; + } + + //this status has a higher sorting priority than the other status + + //this status is not done or in progress and the other status is done + if ((toJsonString().equalsIgnoreCase("N") || toJsonString().equalsIgnoreCase("I")) + && other.toJsonString().equalsIgnoreCase("D")) { + return -1; + } + //this status is not done and the other is in progress + if (toJsonString().equalsIgnoreCase("N") + && other.toJsonString().equalsIgnoreCase("I")) { + return -1; + } + + // this status has a lower sorting priority than the other status + return 1; + } + +} diff --git a/src/main/java/trackr/model/order/OrderUtil.java b/src/main/java/trackr/model/order/OrderUtil.java new file mode 100644 index 00000000000..2ca88f0664d --- /dev/null +++ b/src/main/java/trackr/model/order/OrderUtil.java @@ -0,0 +1,52 @@ +package trackr.model.order; + +import trackr.model.menu.ItemPrice; +import trackr.model.menu.ItemProfit; +import trackr.model.menu.ItemSellingPrice; +import trackr.model.menu.MenuItem; + +//@author arkarsg +/** + * Wrapper class to calculate financials based on Order and MenuItem. + */ +public class OrderUtil { + + /** + * Calculates the total profit for the given order quantity and menu item. + * + * @param qty The order quantity. + * @param item The menu item. + * @return The total profit as an ItemProfit object. + */ + public static ItemProfit getTotalProfit(OrderQuantity qty, MenuItem item) { + int q = qty.getOrderQuantity(); + Double profit = q * item.getItemProfit().getValue(); + return new ItemProfit(profit); + } + + /** + * Calculates the total cost for the given order quantity and menu item. + * + * @param qty The order quantity. + * @param item The menu item. + * @return The total cost as an ItemPrice object. + */ + public static ItemPrice getTotalCost(OrderQuantity qty, MenuItem item) { + int q = qty.getOrderQuantity(); + Double cost = q * item.getItemCost().getValue(); + return new ItemPrice(cost); + } + + /** + * Calculates the total revenue for the given order quantity and menu item. + * + * @param qty The order quantity. + * @param item The menu item. + * @return The total revenue as an ItemSellingPrice object. + */ + public static ItemSellingPrice getTotalRevenue(OrderQuantity qty, MenuItem item) { + int q = qty.getOrderQuantity(); + Double revenue = q * item.getItemPrice().getValue(); + return new ItemSellingPrice(revenue); + } +} diff --git a/src/main/java/trackr/model/order/SortOrdersComparator.java b/src/main/java/trackr/model/order/SortOrdersComparator.java new file mode 100644 index 00000000000..de497dd0392 --- /dev/null +++ b/src/main/java/trackr/model/order/SortOrdersComparator.java @@ -0,0 +1,54 @@ +package trackr.model.order; + +import static trackr.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Comparator; + +import trackr.logic.parser.CriteriaEnum; + +/** + * Comparator used to sort orders. + */ +public class SortOrdersComparator implements Comparator { + /** + * The criteria to sort the orders by. + */ + private CriteriaEnum criteria; + + /** + * Constructs SortOrdersComparator. + */ + public SortOrdersComparator() { + super(); + } + + public void setCriteria(CriteriaEnum criteria) { + this.criteria = criteria; + } + + /** + * Compares two given orders. + * + * @param order1 the first order to be compared. + * @param order2 the second order to be compared. + * @return -1, 0 or 1 based on the sorting criteria. + */ + @Override + public int compare(Order order1, Order order2) { + requireAllNonNull(order1, order2); + + switch(criteria) { + case TIME_ADDED: + return order1.compareTimeAdded(order2); + case DEADLINE: + return order1.compareDeadline(order2); + case STATUS: + return order1.compareStatus(order2); + case NAME: + return order1.compareName(order2); + case STATUS_AND_DEADLINE: + default: + return order1.compareStatusAndDeadline(order2); + } + } +} diff --git a/src/main/java/trackr/model/order/UniqueOrderList.java b/src/main/java/trackr/model/order/UniqueOrderList.java new file mode 100644 index 00000000000..2e6302326b9 --- /dev/null +++ b/src/main/java/trackr/model/order/UniqueOrderList.java @@ -0,0 +1,24 @@ +package trackr.model.order; + +import trackr.model.ModelEnum; +import trackr.model.item.Item; +import trackr.model.item.UniqueItemList; + +/** + * A list of orders that enforces uniqueness between its elements and does not allow nulls. + * an order is considered unique by comparing using {@code Order#isSameItem(Item)}. As such, adding and updating of + * orders uses Order#isSameItem(Item) for equality to ensure that the Order being added or updated is unique + * in terms of identity in the UniqueOrderList. However, the removal of an Order uses Order#equals(Object) to + * ensure that the Order with exactly the same fields will be removed. + *

+ * Supports a minimal set of list operations. + * + * @see Order#isSameItem(Item) + */ +//@@author liumc-sg-reused +public class UniqueOrderList extends UniqueItemList { + + public UniqueOrderList() { + super(ModelEnum.TASK); + } +} diff --git a/src/main/java/trackr/model/person/Customer.java b/src/main/java/trackr/model/person/Customer.java new file mode 100644 index 00000000000..5b3d1c070be --- /dev/null +++ b/src/main/java/trackr/model/person/Customer.java @@ -0,0 +1,47 @@ +package trackr.model.person; + +import java.util.HashSet; +import java.util.Set; + +import trackr.model.ModelEnum; +import trackr.model.commons.Tag; + +/** + * Represents a Customer. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Customer extends Person { + + private static final PersonEmail EMPTY_EMAIL = new PersonEmail("dummy@dummy"); + private static final Set EMPTY_TAGS = new HashSet<>(); + + /** + * Every field must be present and not null. + */ + public Customer(PersonName customerName, PersonPhone customerPhone, PersonAddress customerAddress) { + super(customerName, customerPhone, EMPTY_EMAIL, customerAddress, EMPTY_TAGS, ModelEnum.CUSTOMER); + } + + public PersonName getCustomerName() { + return getPersonName(); + } + + public PersonPhone getCustomerPhone() { + return getPersonPhone(); + } + + public PersonAddress getCustomerAddress() { + return getPersonAddress(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getPersonName()) + .append("; Phone: ") + .append(getPersonPhone()) + .append("; Address: ") + .append(getPersonAddress()); + return builder.toString(); + } +} diff --git a/src/main/java/trackr/model/person/Person.java b/src/main/java/trackr/model/person/Person.java new file mode 100644 index 00000000000..1c96bd48a95 --- /dev/null +++ b/src/main/java/trackr/model/person/Person.java @@ -0,0 +1,135 @@ +package trackr.model.person; + +import static trackr.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import trackr.model.ModelEnum; +import trackr.model.commons.Tag; +import trackr.model.item.Item; + +/** + * Represents a Person. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +//@@author liumc-sg-reused +public abstract class Person extends Item { + + // Identity fields + private final PersonName personName; + private final PersonPhone personPhone; + private final PersonEmail personEmail; + + // Data fields + private final PersonAddress personAddress; + private final Set personTags = new HashSet<>(); + + /** + * Every field must be present and not null. + */ + public Person(PersonName personName, PersonPhone personPhone, PersonEmail personEmail, PersonAddress personAddress, + Set personTags, ModelEnum modelEnum) { + super(modelEnum); + requireAllNonNull(personName, personPhone, personEmail, personAddress, personTags); + this.personName = personName; + this.personPhone = personPhone; + this.personEmail = personEmail; + this.personAddress = personAddress; + this.personTags.addAll(personTags); + } + + public PersonName getPersonName() { + return personName; + } + + public PersonPhone getPersonPhone() { + return personPhone; + } + + public PersonEmail getPersonEmail() { + return personEmail; + } + + public PersonAddress getPersonAddress() { + return personAddress; + } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public Set getPersonTags() { + return Collections.unmodifiableSet(personTags); + } + + /** + * Returns true if both people have the same name. + * This defines a weaker notion of equality between two people. + */ + @Override + public boolean isSameItem(Item otherItem) { + if (otherItem == this) { + return true; + } + + if (!(otherItem instanceof Person)) { + return false; + } + + Person otherPerson = (Person) otherItem; + + return otherPerson != null + && otherPerson.getPersonPhone().equals(getPersonPhone()); + } + + /** + * Returns true if both people have the same identity and data fields. + * This defines a stronger notion of equality between two people. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Person)) { + return false; + } + + Person otherPerson = (Person) other; + return otherPerson.getPersonName().equals(getPersonName()) + && otherPerson.getPersonPhone().equals(getPersonPhone()) + && otherPerson.getPersonEmail().equals(getPersonEmail()) + && otherPerson.getPersonAddress().equals(getPersonAddress()) + && otherPerson.getPersonTags().equals(getPersonTags()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(personName, personPhone, personEmail, personAddress, personTags); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getPersonName()) + .append("; Phone: ") + .append(getPersonPhone()) + .append("; Email: ") + .append(getPersonEmail()) + .append("; Address: ") + .append(getPersonAddress()); + + Set tags = getPersonTags(); + if (!tags.isEmpty()) { + builder.append("; Tags: "); + tags.forEach(builder::append); + } + return builder.toString(); + } + +} diff --git a/src/main/java/trackr/model/person/PersonAddress.java b/src/main/java/trackr/model/person/PersonAddress.java new file mode 100644 index 00000000000..62a9dba9152 --- /dev/null +++ b/src/main/java/trackr/model/person/PersonAddress.java @@ -0,0 +1,58 @@ +package trackr.model.person; + +import static java.util.Objects.requireNonNull; +import static trackr.commons.util.AppUtil.checkArgument; + +/** + * Represents a Person's address. + * Guarantees: immutable; is valid as declared in {@link #isValidPersonAddress(String)} + */ +//@@author liumc-sg-reused +public class PersonAddress { + + 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, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[^\\s].*"; + + public final String personAddress; + + /** + * Constructs an {@code PersonAddress}. + * + * @param personAddress A valid person address. + */ + public PersonAddress(String personAddress) { + requireNonNull(personAddress); + checkArgument(isValidPersonAddress(personAddress), MESSAGE_CONSTRAINTS); + this.personAddress = personAddress; + } + + /** + * Returns true if a given string is a valid person address. + */ + public static boolean isValidPersonAddress(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return personAddress; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof PersonAddress // instanceof handles nulls + && personAddress.equals(((PersonAddress) other).personAddress)); // state check + } + + @Override + public int hashCode() { + return personAddress.hashCode(); + } + +} diff --git a/src/main/java/trackr/model/person/PersonDescriptor.java b/src/main/java/trackr/model/person/PersonDescriptor.java new file mode 100644 index 00000000000..268386d8326 --- /dev/null +++ b/src/main/java/trackr/model/person/PersonDescriptor.java @@ -0,0 +1,116 @@ +package trackr.model.person; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import trackr.commons.util.CollectionUtil; +import trackr.model.commons.Tag; +import trackr.model.item.ItemDescriptor; + +/** + * Stores the details to edit the supplier with. Each non-empty field value will replace the corresponding field + * value of the person. + */ +//@@author liumc-sg-reused +public class PersonDescriptor implements ItemDescriptor { + private PersonName name; + private PersonPhone personPhone; + private PersonEmail personEmail; + private PersonAddress personAddress; + private Set tags; + + public PersonDescriptor() { + } + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public PersonDescriptor(PersonDescriptor toCopy) { + setName(toCopy.name); + setPhone(toCopy.personPhone); + setEmail(toCopy.personEmail); + setAddress(toCopy.personAddress); + setTags(toCopy.tags); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(name, personPhone, personEmail, personAddress, tags); + } + + public void setName(PersonName name) { + this.name = name; + } + + public Optional getName() { + return Optional.ofNullable(name); + } + + public void setPhone(PersonPhone personPhone) { + this.personPhone = personPhone; + } + + public Optional getPhone() { + return Optional.ofNullable(personPhone); + } + + public void setEmail(PersonEmail personEmail) { + this.personEmail = personEmail; + } + + public Optional getEmail() { + return Optional.ofNullable(personEmail); + } + + public void setAddress(PersonAddress personAddress) { + this.personAddress = personAddress; + } + + public Optional getAddress() { + return Optional.ofNullable(personAddress); + } + + /** + * 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 PersonDescriptor)) { + return false; + } + + // state check + PersonDescriptor otherPersonDescriptor = (PersonDescriptor) other; + + return getName().equals(otherPersonDescriptor.getName()) + && getPhone().equals(otherPersonDescriptor.getPhone()) + && getEmail().equals(otherPersonDescriptor.getEmail()) + && getAddress().equals(otherPersonDescriptor.getAddress()) + && getTags().equals(otherPersonDescriptor.getTags()); + } +} diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/trackr/model/person/PersonEmail.java similarity index 71% rename from src/main/java/seedu/address/model/person/Email.java rename to src/main/java/trackr/model/person/PersonEmail.java index f866e7133de..3044de06f8c 100644 --- a/src/main/java/seedu/address/model/person/Email.java +++ b/src/main/java/trackr/model/person/PersonEmail.java @@ -1,13 +1,13 @@ -package seedu.address.model.person; +package trackr.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static trackr.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 Person's email. + * Guarantees: immutable; is valid as declared in {@link #isValidPersonEmail(String)} */ -public class Email { +public class PersonEmail { private static final String SPECIAL_CHARACTERS = "+_.-"; public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain " @@ -31,41 +31,45 @@ public class Email { private static final String DOMAIN_REGEX = "(" + DOMAIN_PART_REGEX + "\\.)*" + DOMAIN_LAST_PART_REGEX; public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_REGEX; - public final String value; + public final String personEmail; + //@@author liumc-sg-reused /** - * Constructs an {@code Email}. + * Constructs an {@code PersonEmail}. * - * @param email A valid email address. + * @param personEmail A valid person email address. */ - public Email(String email) { - requireNonNull(email); - checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS); - value = email; + public PersonEmail(String personEmail) { + requireNonNull(personEmail); + checkArgument(isValidPersonEmail(personEmail), MESSAGE_CONSTRAINTS); + this.personEmail = personEmail; } + //@@author /** * Returns if a given string is a valid email. */ - public static boolean isValidEmail(String test) { + public static boolean isValidPersonEmail(String test) { return test.matches(VALIDATION_REGEX); } @Override public String toString() { - return value; + return personEmail; } + //@@author liumc-sg-reused @Override public boolean equals(Object other) { return other == this // short circuit if same object - || (other instanceof Email // instanceof handles nulls - && value.equals(((Email) other).value)); // state check + || (other instanceof PersonEmail // instanceof handles nulls + && personEmail.equals(((PersonEmail) other).personEmail)); // state check } + //@@author @Override public int hashCode() { - return value.hashCode(); + return personEmail.hashCode(); } } diff --git a/src/main/java/trackr/model/person/PersonName.java b/src/main/java/trackr/model/person/PersonName.java new file mode 100644 index 00000000000..3e57fe814cd --- /dev/null +++ b/src/main/java/trackr/model/person/PersonName.java @@ -0,0 +1,26 @@ +package trackr.model.person; + +import trackr.model.commons.Name; + +/** + * Represents a Person's name. + * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} + */ +public class PersonName extends Name { + + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + public static final String MESSAGE_CONSTRAINTS = + "Person (Customer / Supplier) names should only contain " + + "alphanumeric characters and spaces, and it should not be blank"; + //@@author + + /** + * Constructs a {@code PersonName}. + * + * @param personName A valid person name. + */ + public PersonName(String personName) { + super(personName, "Person"); + } +} diff --git a/src/main/java/trackr/model/person/PersonNameContainsKeywordsPredicate.java b/src/main/java/trackr/model/person/PersonNameContainsKeywordsPredicate.java new file mode 100644 index 00000000000..a7151ced64d --- /dev/null +++ b/src/main/java/trackr/model/person/PersonNameContainsKeywordsPredicate.java @@ -0,0 +1,39 @@ +package trackr.model.person; + +import java.util.List; +import java.util.function.Predicate; + +import trackr.commons.util.StringUtil; +import trackr.model.item.Item; + +/** + * Tests that a {@code Suppliers}'s {@code Name} matches any of the keywords given. + */ +//@@author liumc-sg-reused +public class PersonNameContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public PersonNameContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Item item) { + if (!(item instanceof Supplier)) { + return false; + } + + Supplier supplier = (Supplier) item; + + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(supplier.getPersonName().getName(), keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof PersonNameContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((PersonNameContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/trackr/model/person/PersonPhone.java b/src/main/java/trackr/model/person/PersonPhone.java new file mode 100644 index 00000000000..5a3eb858bd0 --- /dev/null +++ b/src/main/java/trackr/model/person/PersonPhone.java @@ -0,0 +1,54 @@ +package trackr.model.person; + +import static java.util.Objects.requireNonNull; +import static trackr.commons.util.AppUtil.checkArgument; + +/** + * Represents a Person's phone number. + * Guarantees: immutable; is valid as declared in {@link #isValidPersonPhone(String)} + */ +//@@author liumc-sg-reused +public class PersonPhone { + + public static final String MESSAGE_CONSTRAINTS = + "Phone numbers should only contain numbers, and it should at least 3 digits long"; + public static final String VALIDATION_REGEX = "\\d{3,}"; + + public final String personPhone; + + /** + * Constructs a {@code PersonPhone}. + * + * @param personPhone A valid person phone number. + */ + public PersonPhone(String personPhone) { + requireNonNull(personPhone); + checkArgument(isValidPersonPhone(personPhone), MESSAGE_CONSTRAINTS); + this.personPhone = personPhone; + } + + /** + * Returns true if a given string is a valid phone number. + */ + public static boolean isValidPersonPhone(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return personPhone; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof PersonPhone // instanceof handles nulls + && personPhone.equals(((PersonPhone) other).personPhone)); // state check + } + + @Override + public int hashCode() { + return personPhone.hashCode(); + } + +} diff --git a/src/main/java/trackr/model/person/Supplier.java b/src/main/java/trackr/model/person/Supplier.java new file mode 100644 index 00000000000..88d334f6216 --- /dev/null +++ b/src/main/java/trackr/model/person/Supplier.java @@ -0,0 +1,22 @@ +package trackr.model.person; + +import java.util.Set; + +import trackr.model.ModelEnum; +import trackr.model.commons.Tag; + +/** + * Represents a Supplier in the supplier list. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Supplier extends Person { + + /** + * Every field must be present and not null. + */ + public Supplier(PersonName personName, PersonPhone personPhone, PersonEmail personEmail, + PersonAddress personAddress, Set tags) { + super(personName, personPhone, personEmail, personAddress, tags, ModelEnum.SUPPLIER); + } + +} diff --git a/src/main/java/trackr/model/person/UniqueSupplierList.java b/src/main/java/trackr/model/person/UniqueSupplierList.java new file mode 100644 index 00000000000..d857b2ea0e9 --- /dev/null +++ b/src/main/java/trackr/model/person/UniqueSupplierList.java @@ -0,0 +1,24 @@ +package trackr.model.person; + +import trackr.model.ModelEnum; +import trackr.model.item.Item; +import trackr.model.item.UniqueItemList; + +/** + * A list of suppliers that enforces uniqueness between its elements and does not allow nulls. + * A supplier is considered unique by comparing using {@code Supplier#isSameItem(Item)}. As such, adding and updating of + * suppliers uses Supplier#isSameItem(Item) for equality to ensure that the supplier being added or updated is unique + * in terms of identity in the UniqueSupplierList. However, the removal of a supplier uses Supplier#equals(Object) to + * ensure that the supplier with exactly the same fields will be removed. + *

+ * Supports a minimal set of list operations. + * + * @see Supplier#isSameItem(Item) + */ +//@@author liumc-sg-reused +public class UniqueSupplierList extends UniqueItemList { + + public UniqueSupplierList() { + super(ModelEnum.SUPPLIER); + } +} diff --git a/src/main/java/trackr/model/task/SortTasksComparator.java b/src/main/java/trackr/model/task/SortTasksComparator.java new file mode 100644 index 00000000000..5205f3e780f --- /dev/null +++ b/src/main/java/trackr/model/task/SortTasksComparator.java @@ -0,0 +1,54 @@ +package trackr.model.task; + +import static trackr.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Comparator; + +import trackr.logic.parser.CriteriaEnum; + +/** + * Comparator used to sort tasks. + */ +public class SortTasksComparator implements Comparator { + /** + * The criteria to sort the tasks by. + */ + private CriteriaEnum criteria; + + /** + * Constructs SortTasksComparator. + */ + public SortTasksComparator() { + super(); + } + + public void setCriteria(CriteriaEnum criteria) { + this.criteria = criteria; + } + + /** + * Compares two given tasks. + * + * @param task1 the first task to be compared. + * @param task2 the second task to be compared. + * @return -1, 0 or 1 based on the sorting criteria. + */ + @Override + public int compare(Task task1, Task task2) { + requireAllNonNull(task1, task2); + + switch(criteria) { + case TIME_ADDED: + return task1.compareTimeAdded(task2); + case DEADLINE: + return task1.compareDeadline(task2); + case STATUS: + return task1.compareStatus(task2); + case NAME: + return task1.compareName(task2); + case STATUS_AND_DEADLINE: + default: + return task1.compareStatusAndDeadline(task2); + } + } +} diff --git a/src/main/java/trackr/model/task/Task.java b/src/main/java/trackr/model/task/Task.java new file mode 100644 index 00000000000..60a30e81b39 --- /dev/null +++ b/src/main/java/trackr/model/task/Task.java @@ -0,0 +1,195 @@ +package trackr.model.task; + +import static trackr.commons.util.CollectionUtil.requireAllNonNull; + +import java.time.LocalDateTime; +import java.util.Objects; + +import trackr.model.ModelEnum; +import trackr.model.item.Item; + +/** + * Represents a Task in the task list. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Task extends Item { + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + + // Data fields + private final TaskName taskName; + private final TaskDeadline taskDeadline; + private final TaskStatus taskStatus; + private final LocalDateTime timeAdded; + + /** + * Every field must be present and not null. + */ + public Task(TaskName taskName, TaskDeadline taskDeadline, TaskStatus taskStatus) { + super(ModelEnum.TASK); + requireAllNonNull(taskName, taskDeadline, taskStatus); + this.taskName = taskName; + this.taskDeadline = taskDeadline; + this.taskStatus = taskStatus; + timeAdded = LocalDateTime.now(); + } + + /** + * Every field must be present and not null. + */ + public Task(TaskName taskName, TaskDeadline taskDeadline, TaskStatus taskStatus, LocalDateTime timeAdded) { + super(ModelEnum.TASK); + requireAllNonNull(taskName, taskDeadline, taskStatus); + this.taskName = taskName; + this.taskDeadline = taskDeadline; + this.taskStatus = taskStatus; + this.timeAdded = timeAdded; + } + + public TaskName getTaskName() { + return taskName; + } + + public TaskDeadline getTaskDeadline() { + return taskDeadline; + } + + public TaskStatus getTaskStatus() { + return taskStatus; + } + + public LocalDateTime getTimeAdded() { + return timeAdded; + } + //@@author + + /** + * Compares 2 tasks using their time added. + * + * @param otherTask The task to compare with. + * @return -1 if this task was added earlier than the other task. + * Returns 1 if this task was added later than the other task + * Returns 0 if both tasks were added at the same time. + */ + public int compareTimeAdded(Task otherTask) { + return timeAdded.compareTo(otherTask.timeAdded); + } + + /** + * Compares 2 tasks using their names (ignoring case). + * + * @param otherTask The task to compare with. + * @return 1 if this name is lexicographically larger (ignoring case) than the other name, + * -1 if this name is lexicographically smaller (ignoring case) than the other name, + * 0 if both names are lexicographically (ignoring case) equal. + */ + public int compareName(Task otherTask) { + return taskName.compare(otherTask.getTaskName()); + } + + /** + * Compares 2 tasks using their deadlines. + * + * @param otherTask The task to compare with. + * @return -1 if this task has an earlier deadline than the other task. + * Returns 1 if this task has a later deadline than the other task + * Returns 0 if both tasks have the same deadlines. + */ + public int compareDeadline(Task otherTask) { + return taskDeadline.compare(otherTask.getTaskDeadline()); + } + + /** + * Compares 2 tasks using their statuses. + * + * @param otherTask The task to compare with. + * @return -1 if this task is not done while the other task is done, + * Returns 1 if this task is done while the other task is not done, + * Returns 0 if both tasks have the same statuses. + */ + public int compareStatus(Task otherTask) { + return taskStatus.compare(otherTask.getTaskStatus()); + } + + /** + * Compares 2 tasks using their statuses and deadlines. + * + * @param otherTask The task to compare with. + * @return -1 if this task is not done while the other task is done, + * or if both have the same status but this task has an earlier deadline. + * Returns 1 if this task is done while the other task is not done, + * or if both have the same status but this task has a later deadline. + * Returns 0 if both tasks have the same statuses and deadlines. + */ + public int compareStatusAndDeadline(Task otherTask) { + int compareStatusResult = taskStatus.compare(otherTask.getTaskStatus()); + + if (compareStatusResult != 0) { + return compareStatusResult; + } + //both task statuses are equal so compare deadline + return taskDeadline.compare(otherTask.getTaskDeadline()); + } + + //@@author HmuuMyatMoe-reused + // Reused from AB3 with minor modifications + /** + * Returns true if both tasks have the same name and deadline. + * This defines a weaker notion of equality between two tasks. + */ + @Override + public boolean isSameItem(Item otherItem) { + if (otherItem == this) { + return true; + } + + if (!(otherItem instanceof Task)) { + return false; + } + + Task otherTask = (Task) otherItem; + + return otherTask != null + && otherTask.getTaskName().equals(getTaskName()) + && otherTask.getTaskDeadline().equals(getTaskDeadline()); + } + + /** + * Returns true if both tasks have the same name, deadline and status. + * This defines a stronger notion of equality between two tasks. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Task)) { + return false; + } + + Task otherTask = (Task) other; + return otherTask.getTaskName().equals(getTaskName()) + && otherTask.getTaskDeadline().equals(getTaskDeadline()) + && otherTask.getTaskStatus().equals(getTaskStatus()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(taskName, taskDeadline, taskStatus); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getTaskName()) + .append("; Deadline: ") + .append(getTaskDeadline()) + .append("; Status: ") + .append(getTaskStatus()); + return builder.toString(); + } + //@@author +} + diff --git a/src/main/java/trackr/model/task/TaskContainsKeywordsPredicate.java b/src/main/java/trackr/model/task/TaskContainsKeywordsPredicate.java new file mode 100644 index 00000000000..bb11ef2ec18 --- /dev/null +++ b/src/main/java/trackr/model/task/TaskContainsKeywordsPredicate.java @@ -0,0 +1,108 @@ +package trackr.model.task; + +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +import trackr.commons.util.StringUtil; +import trackr.model.item.Item; + +/** + * Tests that a {@code Task}'s {@code TaskName}, {@code TaskDeadline}, {@code TaskStatus} matches the keywords given. + */ + +//@@author liumc-sg-reused +public class TaskContainsKeywordsPredicate extends TaskDescriptor implements Predicate { + private List taskNameKeywords; + + public TaskContainsKeywordsPredicate() { + super(); + } + + /** + * Constructs a new {@code TaskContainsKeywordsPredicate} object with the same keywords of task details as the + * {@code OrderContainsKeywordsPredicate} object specified. + * + * @param toCopy The {@code TaskContainsKeywordsPredicate} object to copy the task name keywords from. + */ + public TaskContainsKeywordsPredicate(TaskContainsKeywordsPredicate toCopy) { + setTaskNameKeywords(toCopy.taskNameKeywords); + setTaskDeadline(toCopy.getTaskDeadline().isPresent() ? toCopy.getTaskDeadline().get() : null); + setTaskStatus(toCopy.getTaskStatus().isPresent() ? toCopy.getTaskStatus().get() : null); + } + + public void setTaskNameKeywords(List taskNameKeywords) { + this.taskNameKeywords = taskNameKeywords; + } + + public Optional> getTaskNameKeywords() { + return Optional.ofNullable(taskNameKeywords); + } + + /** + * Returns true if any of the fields in the {@code Task} object are present or not. + * + * @return True if any of the fields in the {@code Task} object are present or not. + */ + public boolean isAnyFieldPresent() { + return isAnyFieldNonNull() || taskNameKeywords != null; + } + + @Override + public boolean test(Item item) { + if (!(item instanceof Task)) { + return false; + } + + Task task = (Task) item; + + boolean isTaskNameMatch; + boolean isTaskDeadlineMatch; + boolean isTaskStatusMatch; + + if (taskNameKeywords != null) { + isTaskNameMatch = taskNameKeywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(task.getTaskName().getName(), keyword)); + } else { + // Default value since no name keyword is set + isTaskNameMatch = true; + } + + if (getTaskDeadline().isPresent()) { + isTaskDeadlineMatch = getTaskDeadline().get().equals(task.getTaskDeadline()); + } else { + // Default value since no deadline keyword is set + isTaskDeadlineMatch = true; + } + + if (getTaskStatus().isPresent()) { + isTaskStatusMatch = getTaskStatus().get().equals(task.getTaskStatus()); + } else { + // Default value since no status keyword is set + isTaskStatusMatch = true; + } + + return isTaskNameMatch && isTaskDeadlineMatch && isTaskStatusMatch; + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof TaskContainsKeywordsPredicate)) { + return false; + } + + // state check + TaskContainsKeywordsPredicate predicate = (TaskContainsKeywordsPredicate) other; + + return getTaskNameKeywords().equals(predicate.getTaskNameKeywords()) + && getTaskDeadline().equals(predicate.getTaskDeadline()) + && getTaskStatus().equals(predicate.getTaskStatus()); + } + +} diff --git a/src/main/java/trackr/model/task/TaskDeadline.java b/src/main/java/trackr/model/task/TaskDeadline.java new file mode 100644 index 00000000000..2d6c5493985 --- /dev/null +++ b/src/main/java/trackr/model/task/TaskDeadline.java @@ -0,0 +1,26 @@ +package trackr.model.task; + +import trackr.model.commons.Deadline; + +/** + * Represents a Task's deadline in the task list. + * Guarantees: immutable; is valid as declared in {@link #isValidDeadline(String)} + */ +public class TaskDeadline extends Deadline { + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + public static final String MESSAGE_CONSTRAINTS = + "Task deadline should only contain numeric values in the format \"DD/MM/YYYY\" and it should not be blank."; + + + /** + * Constructs a {@code TaskDeadline}. + * + * @param deadline A valid deadline. + */ + public TaskDeadline(String deadline) { + super(deadline, "Task"); + } + //@@author +} + diff --git a/src/main/java/trackr/model/task/TaskDescriptor.java b/src/main/java/trackr/model/task/TaskDescriptor.java new file mode 100644 index 00000000000..75e9e2d98dd --- /dev/null +++ b/src/main/java/trackr/model/task/TaskDescriptor.java @@ -0,0 +1,78 @@ +package trackr.model.task; + +import java.util.Optional; + +import trackr.commons.util.CollectionUtil; +import trackr.model.item.ItemDescriptor; + +/** + * Stores the details of a task. Each non-empty field value will replace the corresponding field value of the task. + */ +//@@author liumc-sg-reused +public class TaskDescriptor implements ItemDescriptor { + private TaskName taskName; + private TaskDeadline taskDeadline; + private TaskStatus taskStatus; + + public TaskDescriptor() {} + + /** + * Copy constructor. + */ + public TaskDescriptor(TaskDescriptor toCopy) { + setTaskName(toCopy.taskName); + setTaskDeadline(toCopy.taskDeadline); + setTaskStatus(toCopy.taskStatus); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldNonNull() { + return CollectionUtil.isAnyNonNull(taskName, taskDeadline, taskStatus); + } + + public void setTaskName(TaskName taskName) { + this.taskName = taskName; + } + + public Optional getTaskName() { + return Optional.ofNullable(taskName); + } + + public void setTaskDeadline(TaskDeadline taskDeadline) { + this.taskDeadline = taskDeadline; + } + + public Optional getTaskDeadline() { + return Optional.ofNullable(taskDeadline); + } + + public void setTaskStatus(TaskStatus taskStatus) { + this.taskStatus = taskStatus; + } + + public Optional getTaskStatus() { + return Optional.ofNullable(taskStatus); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof TaskDescriptor)) { + return false; + } + + // state check + TaskDescriptor taskDescriptor = (TaskDescriptor) other; + + return getTaskName().equals(taskDescriptor.getTaskName()) + && getTaskDeadline().equals(taskDescriptor.getTaskDeadline()) + && getTaskStatus().equals(taskDescriptor.getTaskStatus()); + } +} diff --git a/src/main/java/trackr/model/task/TaskName.java b/src/main/java/trackr/model/task/TaskName.java new file mode 100644 index 00000000000..6e13ebbb34e --- /dev/null +++ b/src/main/java/trackr/model/task/TaskName.java @@ -0,0 +1,26 @@ +package trackr.model.task; + +import trackr.model.commons.Name; + +/** + * Represents a Task's name in the task list. + * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} + */ +public class TaskName extends Name { + + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + public static final String MESSAGE_CONSTRAINTS = + "Task names should only contain alphanumeric characters and spaces, and it should not be blank"; + + /** + * Constructs a {@code TaskName}. + * + * @param taskName A valid task name. + */ + public TaskName(String taskName) { + super(taskName, "Task"); + } + //@@author +} + diff --git a/src/main/java/trackr/model/task/TaskStatus.java b/src/main/java/trackr/model/task/TaskStatus.java new file mode 100644 index 00000000000..21b05d41ccc --- /dev/null +++ b/src/main/java/trackr/model/task/TaskStatus.java @@ -0,0 +1,75 @@ +package trackr.model.task; + +import java.util.HashMap; +import java.util.Map; + +import trackr.model.commons.Status; + +/** + * Represents a Task's status in the task list. + * Guarantees: immutable; is valid as declared in {@link #isValidStatus(String, HashMap)} + */ +public class TaskStatus extends Status { + + public static final HashMap STATUSES; + public static final String STATUS_MESSAGE; + public static final String MESSAGE_CONSTRAINTS; + + static { + STATUSES = new HashMap<>(); + STATUSES.put("N", "Not Done"); + STATUSES.put("D", "Done"); + + StringBuilder statusMessageBuilder = new StringBuilder(); + for (Map.Entry entry : STATUSES.entrySet()) { + statusMessageBuilder.append(String.format(" `%s` or `%s` for %s,", entry.getKey(), + entry.getKey().toLowerCase(), entry.getValue())); + } + STATUS_MESSAGE = + statusMessageBuilder.deleteCharAt(statusMessageBuilder.length() - 1).append(".").toString(); + MESSAGE_CONSTRAINTS = String.format(Status.MESSAGE_CONSTRAINTS_FORMAT, "Task", STATUS_MESSAGE); + } + + /** + * Constructs a {@code TaskStatus} with a given status input. + * + * @param status A valid status. + */ + public TaskStatus(String status) { + super(status, "Task", STATUSES); + } + + /** + * Constructs a {@code TaskStatus} with no status input given (default case). + */ + public TaskStatus() { + this("N"); + } + + /** + * Compare this status to a given status. + * + * @param other The other task status to compare this status with. + * @return 1 if this status is done and the given status is not done, + * -1 if this status is not done and the given status is done + * 0 if both statuses are the same. + */ + + @Override + public int compare(Status other) { + //this status is done and given status is not done + if (toJsonString().equalsIgnoreCase("D") + && other.toJsonString().equalsIgnoreCase("N")) { + return 1; + } + //this status is not done and given status is done + if (toJsonString().equalsIgnoreCase("N") + && other.toJsonString().equalsIgnoreCase("D")) { + return -1; + } + //both status are the same + return 0; + } + +} + diff --git a/src/main/java/trackr/model/task/UniqueTaskList.java b/src/main/java/trackr/model/task/UniqueTaskList.java new file mode 100644 index 00000000000..726d4249831 --- /dev/null +++ b/src/main/java/trackr/model/task/UniqueTaskList.java @@ -0,0 +1,26 @@ +package trackr.model.task; + +import trackr.model.ModelEnum; +import trackr.model.item.Item; +import trackr.model.item.UniqueItemList; + +//@@author liumc-sg-reused with minor modifications +/** + * A list of tasks that enforces uniqueness between its elements and does not allow nulls. + * A task is considered unique by comparing using {@code Task#isSameItem(Item)}. As such, adding and updating of + * tasks uses Task#isSameItem(Item) for equality to ensure that the task being added or updated is unique + * in terms of identity in the UniqueTaskList. However, the removal of a task uses Task#equals(Object) to + * ensure that the task with exactly the same fields will be removed. + *

+ * Supports a minimal set of list operations. + * + * @see Task#isSameItem(Item) + */ +//@@author liumc-sg-reused +public class UniqueTaskList extends UniqueItemList { + + public UniqueTaskList() { + super(ModelEnum.TASK); + } +} +//@@author diff --git a/src/main/java/trackr/model/util/SampleDataUtil.java b/src/main/java/trackr/model/util/SampleDataUtil.java new file mode 100644 index 00000000000..d695fbfa63d --- /dev/null +++ b/src/main/java/trackr/model/util/SampleDataUtil.java @@ -0,0 +1,170 @@ +package trackr.model.util; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +import trackr.model.Menu; +import trackr.model.OrderList; +import trackr.model.ReadOnlyMenu; +import trackr.model.ReadOnlyOrderList; +import trackr.model.ReadOnlySupplierList; +import trackr.model.ReadOnlyTaskList; +import trackr.model.SupplierList; +import trackr.model.TaskList; +import trackr.model.commons.Tag; +import trackr.model.menu.ItemCost; +import trackr.model.menu.ItemName; +import trackr.model.menu.ItemSellingPrice; +import trackr.model.menu.MenuItem; +import trackr.model.order.Order; +import trackr.model.order.OrderDeadline; +import trackr.model.order.OrderQuantity; +import trackr.model.order.OrderStatus; +import trackr.model.person.Customer; +import trackr.model.person.PersonAddress; +import trackr.model.person.PersonEmail; +import trackr.model.person.PersonName; +import trackr.model.person.PersonPhone; +import trackr.model.person.Supplier; +import trackr.model.task.Task; +import trackr.model.task.TaskDeadline; +import trackr.model.task.TaskName; +import trackr.model.task.TaskStatus; + +/** + * Contains utility methods for populating {@code Trackr} with sample data. + */ +public class SampleDataUtil { + public static Supplier[] getSampleSuppliers() { + return new Supplier[] { + new Supplier(new PersonName("Prima Flour"), new PersonPhone("87438807"), + new PersonEmail("sales.primaflour@prima.com.sg"), new PersonAddress("Blk 30 Geylang Street 29, #06-40"), + getTagSet("flour")), + new Supplier(new PersonName("Kim Guan Guan Coffee"), new PersonPhone("99272758"), + new PersonEmail("info@kimguanguan.com"), new PersonAddress("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), + getTagSet("coffee")), + new Supplier(new PersonName("N Supplies"), new PersonPhone("93210283"), + new PersonEmail("sales@nsupplies.com"), new PersonAddress("Blk 11 Ang Mo Kio Street 74, #11-04"), + getTagSet("halal")), + new Supplier(new PersonName("Teck Leong Pte Ltd"), new PersonPhone("91031282"), + new PersonEmail("teckleong@example.com"), + new PersonAddress("Blk 436 Serangoon Gardens Street 26, #16-43"), getTagSet("confectionery")), + new Supplier(new PersonName("Chip Seng"), new PersonPhone("92492021"), + new PersonEmail("chipseng@example.com"), new PersonAddress("Blk 47 Tampines Street 20, #17-35"), + getTagSet("sugar")), + new Supplier(new PersonName("Roy Selva"), new PersonPhone("92624417"), new PersonEmail("roys@example.com"), + new PersonAddress("Blk 45 Aljunied Street 85, #11-31"), + getTagSet("driver")) + }; + } + + public static ReadOnlySupplierList getSampleSupplierList() { + SupplierList sampleSupplierList = new SupplierList(); + for (Supplier sampleSupplier : getSampleSuppliers()) { + sampleSupplierList.addItem(sampleSupplier); + } + return sampleSupplierList; + } + + /** + * Returns a tag set containing the list of strings given. + */ + public static Set getTagSet(String... strings) { + return Arrays.stream(strings) + .map(Tag::new) + .collect(Collectors.toSet()); + } + + //@@author HmuuMyatMoe-reused + //Reused from AB3 with minor modifications + public static Task[] getSampleTasks() { + return new Task[] { + new Task(new TaskName("Buy flour"), new TaskDeadline("01/01/2024"), + new TaskStatus()), + new Task(new TaskName("Sort inventory"), new TaskDeadline("03/03/2024"), + new TaskStatus("D")), + new Task(new TaskName("Check status of orders"), new TaskDeadline("02/01/2024"), + new TaskStatus("N")) + }; + } + //@@author + + //@@author liumc-sg-reused + public static ReadOnlyTaskList getSampleTaskList() { + TaskList sampleTl = new TaskList(); + for (Task sampleTask : getSampleTasks()) { + sampleTl.addItem(sampleTask); + } + return sampleTl; + } + //@@author + + //@@author changgittyhub-reused + public static MenuItem[] getSampleMenuItems() { + return new MenuItem[] { + new MenuItem(new ItemName("Chocolate Cookies"), new ItemSellingPrice("5.00"), new ItemCost("1.20")), + new MenuItem(new ItemName("Cupcake"), new ItemSellingPrice("6.20"), new ItemCost("1.10")), + new MenuItem(new ItemName("Bread"), new ItemSellingPrice("10"), new ItemCost("0.5")) + }; + } + + //@@author changgittyhub-reused + public static ReadOnlyMenu getSampleMenu() { + Menu sampleM = new Menu(); + for (MenuItem sampleMenuItem : getSampleMenuItems()) { + sampleM.addItem(sampleMenuItem); + } + return sampleM; + } + + //@@author arkarsg-reused + public static Order[] getSampleOrders() { + Customer amy = new Customer(new PersonName("Amy"), + new PersonPhone("12345678"), + new PersonAddress("123 Smith Street")); + Customer bob = new Customer(new PersonName("Bob"), + new PersonPhone("87654321"), + new PersonAddress("321 Hoover Street")); + Customer charlie = new Customer(new PersonName("Charlie"), + new PersonPhone("71396482"), + new PersonAddress("789 Bonder Street")); + + MenuItem cookie = new MenuItem(new ItemName("Chocolate Cookies"), + new ItemSellingPrice("5.00"), + new ItemCost("1.20")); + MenuItem cupcake = new MenuItem(new ItemName("Cupcake"), + new ItemSellingPrice("6.20"), + new ItemCost("1.10")); + MenuItem bread = new MenuItem(new ItemName("Bread"), + new ItemSellingPrice("10"), + new ItemCost("0.5")); + + return new Order[] { + new Order(cookie, + new OrderDeadline("01/01/2024"), + new OrderStatus(), new OrderQuantity("2"), + amy), + new Order(cupcake, + new OrderDeadline("03/03/2024"), + new OrderStatus("I"), + new OrderQuantity("10"), + bob), + new Order(bread, + new OrderDeadline("02/01/2024"), + new OrderStatus("D"), + new OrderQuantity("100"), + charlie) + }; + } + //@@author + + public static ReadOnlyOrderList getSampleOrderList() { + OrderList sampleOl = new OrderList(); + for (Order sampleOrder : getSampleOrders()) { + sampleOl.addItem(sampleOrder); + } + return sampleOl; + } + +} diff --git a/src/main/java/trackr/storage/JsonAdaptedMenuItem.java b/src/main/java/trackr/storage/JsonAdaptedMenuItem.java new file mode 100644 index 00000000000..51aacb60336 --- /dev/null +++ b/src/main/java/trackr/storage/JsonAdaptedMenuItem.java @@ -0,0 +1,85 @@ +package trackr.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import trackr.commons.exceptions.IllegalValueException; +import trackr.model.menu.ItemCost; +import trackr.model.menu.ItemName; +import trackr.model.menu.ItemSellingPrice; +import trackr.model.menu.MenuItem; + +/** + * Jackson-friendly version of {@link MenuItem}. + */ +//@@author changgittyhub-reused +class JsonAdaptedMenuItem { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Menu Item's %s field is missing!"; + + private final String itemName; + private final String itemCost; + private final String itemPrice; + private final String itemProfit; + + /** + * Constructs a {@code JsonAdaptedMenuItem} with the given menu item details. + */ + @JsonCreator + public JsonAdaptedMenuItem(@JsonProperty("itemName") String itemName, + @JsonProperty("itemCost") String itemCost, + @JsonProperty("itemPrice") String itemPrice, + @JsonProperty("itemProfit") String itemProfit) { + this.itemName = itemName; + this.itemCost = itemCost; + this.itemPrice = itemPrice; + this.itemProfit = itemProfit; + } + + /** + * Converts a given {@code MenuItem} into this class for Jackson use. + */ + public JsonAdaptedMenuItem(MenuItem source) { + itemName = source.getItemName().getName(); + itemCost = source.getItemCost().toJsonString(); + itemPrice = source.getItemPrice().toJsonString(); + itemProfit = source.getItemProfit().toJsonString(); + } + + /** + * Converts this Jackson-friendly adapted menu item object into the model's {@code MenuItem} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted menu item. + */ + public MenuItem toModelType() throws IllegalValueException { + if (itemName == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + ItemName.class.getSimpleName())); + } + if (!ItemName.isValidName(itemName)) { + throw new IllegalValueException(ItemName.MESSAGE_CONSTRAINTS); + } + final ItemName modelItemName = new ItemName(itemName); + + if (itemCost == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + ItemCost.class.getSimpleName())); + } + if (!ItemCost.isValidPrice(itemCost)) { + throw new IllegalValueException(ItemCost.MESSAGE_CONSTRAINTS); + } + final ItemCost modelItemCost = new ItemCost(itemCost); + + if (itemPrice == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + ItemSellingPrice.class.getSimpleName())); + } + if (!ItemSellingPrice.isValidPrice(itemPrice)) { + throw new IllegalValueException(ItemSellingPrice.MESSAGE_CONSTRAINTS); + } + final ItemSellingPrice modelItemPrice = new ItemSellingPrice(itemPrice); + + return new MenuItem(modelItemName, modelItemPrice, modelItemCost); + } + +} diff --git a/src/main/java/trackr/storage/JsonAdaptedOrder.java b/src/main/java/trackr/storage/JsonAdaptedOrder.java new file mode 100644 index 00000000000..e7ed9f795d5 --- /dev/null +++ b/src/main/java/trackr/storage/JsonAdaptedOrder.java @@ -0,0 +1,165 @@ +package trackr.storage; + +import java.time.LocalDateTime; +import java.time.format.DateTimeParseException; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import trackr.commons.exceptions.IllegalValueException; +import trackr.model.menu.MenuItem; +import trackr.model.order.Order; +import trackr.model.order.OrderDeadline; +import trackr.model.order.OrderName; +import trackr.model.order.OrderQuantity; +import trackr.model.order.OrderStatus; +import trackr.model.person.Customer; +import trackr.model.person.PersonAddress; +import trackr.model.person.PersonName; +import trackr.model.person.PersonPhone; + +/** + * Jackson-friendly version of {@link Order}. + */ +public class JsonAdaptedOrder { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Order's %s field is missing!"; + public static final String MESSAGE_PARSE_TIME_ADDED_ERROR = + "Unexpected error encountered when parsing Order's `timeAdded` " + + "field that was read from storage file"; + + + private final String customerName; + private final String customerPhone; + private final String customerAddress; + //@@author arkarsg + private final JsonAdaptedMenuItem menuItem; + //@@author + private final String orderDeadline; + private final String orderQuantity; + private final String orderStatus; + private final String timeAdded; + + //@@author chongweiguan-reused + /** + * Constructs a {@code JsonAdaptedOrder} with the given order details. + */ + @JsonCreator + public JsonAdaptedOrder(@JsonProperty("customerName") String customerName, + @JsonProperty("customerPhone") String customerPhone, + @JsonProperty("customerAddress") String customerAddress, + //@@author arkarsg + @JsonProperty("menuItem") JsonAdaptedMenuItem menuItem, + //@@author + @JsonProperty("orderDeadline") String orderDeadline, + @JsonProperty("orderQuantity") String orderQuantity, + @JsonProperty("orderStatus") String orderStatus, + @JsonProperty("timeAdded") String timeAdded) { + this.customerName = customerName; + this.customerPhone = customerPhone; + this.customerAddress = customerAddress; + this.menuItem = menuItem; + this.orderDeadline = orderDeadline; + this.orderQuantity = orderQuantity; + this.orderStatus = orderStatus; + this.timeAdded = timeAdded; + } + //@@author + + /** + * Converts a given {@code Order} into this class for Jackson use. + */ + public JsonAdaptedOrder(Order source) { + customerName = source.getCustomer().getCustomerName().getName(); + customerPhone = source.getCustomer().getCustomerPhone().personPhone; + customerAddress = source.getCustomer().getCustomerAddress().personAddress; + menuItem = new JsonAdaptedMenuItem(source.getOrderItem()); + orderDeadline = source.getOrderDeadline().toJsonString(); + orderQuantity = source.getOrderQuantity().value; + orderStatus = source.getOrderStatus().toJsonString(); + timeAdded = source.getTimeAdded().toString(); + } + //@@author chongweiguan-reused + /** + * Converts this Jackson-friendly adapted order object into the model's {@code Order} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted order. + */ + public Order toModelType() throws IllegalValueException { + if (customerName == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + PersonName.class.getSimpleName())); + } + if (!PersonName.isValidName(customerName)) { + throw new IllegalValueException(PersonName.MESSAGE_CONSTRAINTS); + } + final PersonName modelName = new PersonName(customerName); + + if (customerPhone == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + PersonPhone.class.getSimpleName())); + } + if (!PersonPhone.isValidPersonPhone(customerPhone)) { + throw new IllegalValueException(PersonPhone.MESSAGE_CONSTRAINTS); + } + final PersonPhone modelPhone = new PersonPhone(customerPhone); + + if (customerAddress == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + PersonAddress.class.getSimpleName())); + } + if (!PersonAddress.isValidPersonAddress(customerAddress)) { + throw new IllegalValueException(PersonAddress.MESSAGE_CONSTRAINTS); + } + final PersonAddress modelAddress = new PersonAddress(customerAddress); + + //@@author arkarsg + if (menuItem == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + OrderName.class.getSimpleName())); + } + + // Guaranteed validity + final MenuItem modelMenuItem = menuItem.toModelType(); + //@@author + + if (orderDeadline == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + OrderDeadline.class.getSimpleName())); + } + if (!OrderDeadline.isValidDeadline(orderDeadline)) { + throw new IllegalValueException(OrderDeadline.MESSAGE_CONSTRAINTS); + } + final OrderDeadline modelOrderDeadline = new OrderDeadline(orderDeadline); + + if (orderQuantity == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + OrderQuantity.class.getSimpleName())); + } + if (!OrderQuantity.isValidQuantity(orderQuantity)) { + throw new IllegalValueException(OrderQuantity.MESSAGE_CONSTRAINTS); + } + final OrderQuantity modelOrderQuantity = new OrderQuantity(orderQuantity); + + if (orderStatus == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + OrderStatus.class.getSimpleName())); + } + if (!OrderStatus.isValidStatus(orderStatus, OrderStatus.STATUSES)) { + throw new IllegalValueException(OrderStatus.MESSAGE_CONSTRAINTS); + } + final OrderStatus modelOrderStatus = new OrderStatus(orderStatus); + + final LocalDateTime modelTimeAdded; + try { + modelTimeAdded = LocalDateTime.parse(timeAdded); + } catch (DateTimeParseException | NullPointerException e) { + throw new IllegalValueException(MESSAGE_PARSE_TIME_ADDED_ERROR); + } + + Customer customer = new Customer(modelName, modelPhone, modelAddress); + return new Order(modelMenuItem, modelOrderDeadline, + modelOrderStatus, modelOrderQuantity, customer, modelTimeAdded); + } + //@@author +} diff --git a/src/main/java/trackr/storage/JsonAdaptedSupplier.java b/src/main/java/trackr/storage/JsonAdaptedSupplier.java new file mode 100644 index 00000000000..e1b45b89c32 --- /dev/null +++ b/src/main/java/trackr/storage/JsonAdaptedSupplier.java @@ -0,0 +1,114 @@ +package trackr.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 trackr.commons.exceptions.IllegalValueException; +import trackr.model.commons.Tag; +import trackr.model.person.PersonAddress; +import trackr.model.person.PersonEmail; +import trackr.model.person.PersonName; +import trackr.model.person.PersonPhone; +import trackr.model.person.Supplier; + +/** + * Jackson-friendly version of {@link Supplier}. + */ +class JsonAdaptedSupplier { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Supplier'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 JsonAdaptedSupplier(@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); + } + } + + //@@author liumc-sg-reused + /** + * Converts a given {@code Supplier} into this class for Jackson use. + */ + public JsonAdaptedSupplier(Supplier source) { + name = source.getPersonName().getName(); + phone = source.getPersonPhone().personPhone; + email = source.getPersonEmail().personEmail; + address = source.getPersonAddress().personAddress; + tagged.addAll(source.getPersonTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList())); + } + + /** + * Converts this Jackson-friendly adapted supplier object into the model's {@code Supplier} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted person. + */ + public Supplier toModelType() throws IllegalValueException { + final List supplierTags = new ArrayList<>(); + for (JsonAdaptedTag tag : tagged) { + supplierTags.add(tag.toModelType()); + } + + if (name == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + PersonName.class.getSimpleName())); + } + if (!PersonName.isValidName(name)) { + throw new IllegalValueException(PersonName.MESSAGE_CONSTRAINTS); + } + final PersonName modelName = new PersonName(name); + + if (phone == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + PersonPhone.class.getSimpleName())); + } + if (!PersonPhone.isValidPersonPhone(phone)) { + throw new IllegalValueException(PersonPhone.MESSAGE_CONSTRAINTS); + } + final PersonPhone modelPersonPhone = new PersonPhone(phone); + + if (email == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + PersonEmail.class.getSimpleName())); + } + if (!PersonEmail.isValidPersonEmail(email)) { + throw new IllegalValueException(PersonEmail.MESSAGE_CONSTRAINTS); + } + final PersonEmail modelPersonEmail = new PersonEmail(email); + + if (address == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + PersonAddress.class.getSimpleName())); + } + if (!PersonAddress.isValidPersonAddress(address)) { + throw new IllegalValueException(PersonAddress.MESSAGE_CONSTRAINTS); + } + final PersonAddress modelPersonAddress = new PersonAddress(address); + + final Set modelTags = new HashSet<>(supplierTags); + return new Supplier(modelName, modelPersonPhone, modelPersonEmail, modelPersonAddress, modelTags); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/trackr/storage/JsonAdaptedTag.java similarity index 88% rename from src/main/java/seedu/address/storage/JsonAdaptedTag.java rename to src/main/java/trackr/storage/JsonAdaptedTag.java index 0df22bdb754..b8fe27c915f 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java +++ b/src/main/java/trackr/storage/JsonAdaptedTag.java @@ -1,10 +1,10 @@ -package seedu.address.storage; +package trackr.storage; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; +import trackr.commons.exceptions.IllegalValueException; +import trackr.model.commons.Tag; /** * Jackson-friendly version of {@link Tag}. @@ -28,11 +28,6 @@ public JsonAdaptedTag(Tag source) { tagName = source.tagName; } - @JsonValue - public String getTagName() { - return tagName; - } - /** * Converts this Jackson-friendly adapted tag object into the model's {@code Tag} object. * @@ -45,4 +40,10 @@ public Tag toModelType() throws IllegalValueException { return new Tag(tagName); } + //@@author liumc-sg-reused + @JsonValue + public String getTagName() { + return tagName; + } + } diff --git a/src/main/java/trackr/storage/JsonAdaptedTask.java b/src/main/java/trackr/storage/JsonAdaptedTask.java new file mode 100644 index 00000000000..27e23cc19f9 --- /dev/null +++ b/src/main/java/trackr/storage/JsonAdaptedTask.java @@ -0,0 +1,97 @@ +package trackr.storage; + +import java.time.LocalDateTime; +import java.time.format.DateTimeParseException; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import trackr.commons.exceptions.IllegalValueException; +import trackr.model.task.Task; +import trackr.model.task.TaskDeadline; +import trackr.model.task.TaskName; +import trackr.model.task.TaskStatus; + +/** + * Jackson-friendly version of {@link Task}. + */ +class JsonAdaptedTask { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Task's %s field is missing!"; + public static final String MESSAGE_PARSE_TIME_ADDED_ERROR = + "Unexpected error encountered when parsing Task's `timeAdded` " + + "field that was read from storage file"; + + private final String taskName; + private final String taskDeadline; + private final String taskStatus; + private final String timeAdded; + + /** + * Constructs a {@code JsonAdaptedTask} with the given task details. + */ + @JsonCreator + public JsonAdaptedTask(@JsonProperty("taskName") String taskName, + @JsonProperty("taskDeadline") String taskDeadline, + @JsonProperty("taskStatus") String taskStatus, + @JsonProperty("timeAdded") String timeAdded) { + this.taskName = taskName; + this.taskDeadline = taskDeadline; + this.taskStatus = taskStatus; + this.timeAdded = timeAdded; + } + + /** + * Converts a given {@code Task} into this class for Jackson use. + */ + public JsonAdaptedTask(Task source) { + taskName = source.getTaskName().getName(); + taskDeadline = source.getTaskDeadline().toJsonString(); + taskStatus = source.getTaskStatus().toJsonString(); + timeAdded = source.getTimeAdded().toString(); + } + + /** + * Converts this Jackson-friendly adapted task object into the model's {@code Task} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted task. + */ + public Task toModelType() throws IllegalValueException { + if (taskName == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + TaskName.class.getSimpleName())); + } + if (!TaskName.isValidName(taskName)) { + throw new IllegalValueException(TaskName.MESSAGE_CONSTRAINTS); + } + final TaskName modelTaskName = new TaskName(taskName); + + if (taskDeadline == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + TaskDeadline.class.getSimpleName())); + } + if (!TaskDeadline.isValidDeadline(taskDeadline)) { + throw new IllegalValueException(TaskDeadline.MESSAGE_CONSTRAINTS); + } + final TaskDeadline modelTaskDeadline = new TaskDeadline(taskDeadline); + + if (taskStatus == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + TaskStatus.class.getSimpleName())); + } + if (!TaskStatus.isValidStatus(taskStatus, TaskStatus.STATUSES)) { + throw new IllegalValueException(TaskStatus.MESSAGE_CONSTRAINTS); + } + final TaskStatus modelTaskStatus = new TaskStatus(taskStatus); + + final LocalDateTime modelTimeAdded; + try { + modelTimeAdded = LocalDateTime.parse(timeAdded); + } catch (DateTimeParseException | NullPointerException e) { + throw new IllegalValueException(MESSAGE_PARSE_TIME_ADDED_ERROR); + } + + return new Task(modelTaskName, modelTaskDeadline, modelTaskStatus, modelTimeAdded); + } + +} diff --git a/src/main/java/trackr/storage/JsonSerializableTrackr.java b/src/main/java/trackr/storage/JsonSerializableTrackr.java new file mode 100644 index 00000000000..9d1e042684e --- /dev/null +++ b/src/main/java/trackr/storage/JsonSerializableTrackr.java @@ -0,0 +1,157 @@ +package trackr.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 trackr.commons.exceptions.IllegalValueException; +import trackr.model.Menu; +import trackr.model.OrderList; +import trackr.model.ReadOnlyMenu; +import trackr.model.ReadOnlyOrderList; +import trackr.model.ReadOnlySupplierList; +import trackr.model.ReadOnlyTaskList; +import trackr.model.SupplierList; +import trackr.model.TaskList; +import trackr.model.menu.MenuItem; +import trackr.model.order.Order; +import trackr.model.person.Supplier; +import trackr.model.task.Task; + +/** + * An Immutable Trackr that is serializable to JSON format. + */ +@JsonRootName(value = "trackr") +class JsonSerializableTrackr { + + public static final String MESSAGE_DUPLICATE_PERSON = "Suppliers list contains duplicate supplier(s)."; + public static final String MESSAGE_DUPLICATE_TASK = "Tasks list contains duplicate task(s)."; + public static final String MESSAGE_DUPLICATE_ORDER = "Orders list contains duplicate order(s)."; + public static final String MESSAGE_DUPLICATE_MENUITEM = "Menu contains duplicate menu item(s)."; + + private final List suppliers = new ArrayList<>(); + private final List tasks = new ArrayList<>(); + private final List orders = new ArrayList<>(); + private final List menuItems = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableTrackr} with the given suppliers, tasks, order and menu items. + */ + @JsonCreator + public JsonSerializableTrackr(@JsonProperty("suppliers") List suppliers, + @JsonProperty("tasks") List tasks, + @JsonProperty("menuItems") List menuItems, + @JsonProperty("orders") List orders) { + this.suppliers.addAll(suppliers); + this.tasks.addAll(tasks); + this.menuItems.addAll(menuItems); + this.orders.addAll(orders); + } + + /** + * Converts a given {@code ReadOnlySupplierList} and {@code ReadOnlyTaskList} into this class for Jackson use. + * + * @param sourceSupplier future changes to this will not affect the created {@code JsonSerializableTrackr}. + * @param sourceTask future changes to this will not affect the created {@code JsonSerializableTrackr}. + * @param sourceOrder future changes to this will not affect the created {@code JsonSerializableTrackr}. + */ + public JsonSerializableTrackr(ReadOnlySupplierList sourceSupplier, ReadOnlyTaskList sourceTask, + ReadOnlyMenu sourceMenuItem, ReadOnlyOrderList sourceOrder) { + suppliers.addAll(sourceSupplier + .getItemList() + .stream() + .map(JsonAdaptedSupplier::new) + .collect(Collectors.toList())); + tasks.addAll(sourceTask + .getItemList() + .stream() + .map(JsonAdaptedTask::new) + .collect(Collectors.toList())); + menuItems.addAll(sourceMenuItem + .getItemList() + .stream() + .map(JsonAdaptedMenuItem::new) + .collect(Collectors.toList())); + orders.addAll(sourceOrder + .getItemList() + .stream() + .map(JsonAdaptedOrder::new) + .collect(Collectors.toList())); + } + + //@@author liumc-sg-reused + /** + * Converts this trackr into the model's {@code SupplierList} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public SupplierList toModelType() throws IllegalValueException { + SupplierList supplierList = new SupplierList(); + for (JsonAdaptedSupplier jsonAdaptedPerson : suppliers) { + Supplier person = jsonAdaptedPerson.toModelType(); + if (supplierList.hasItem(person)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON); + } + supplierList.addItem(person); + } + return supplierList; + } + + /** + * Converts this trackr into the model's {@code TaskList} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public TaskList toTaskModelType() throws IllegalValueException { + TaskList taskList = new TaskList(); + for (JsonAdaptedTask jsonAdaptedTask : tasks) { + Task task = jsonAdaptedTask.toModelType(); + if (taskList.hasItem(task)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_TASK); + } + taskList.addItem(task); + } + return taskList; + } + + //@@author changgittyhub-reused + /** + * Converts this trackr into the model's {@code Menu} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public Menu toMenuItemModelType() throws IllegalValueException { + Menu menu = new Menu(); + for (JsonAdaptedMenuItem jsonAdaptedMenuItem : menuItems) { + MenuItem menuItem = jsonAdaptedMenuItem.toModelType(); + if (menu.hasItem(menuItem)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_MENUITEM); + } + menu.addItem(menuItem); + } + return menu; + } + + //@@author chongweiguan-reused + /** + * Converts this trackr into the model's {@code OrderList} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public OrderList toOrderModelType() throws IllegalValueException { + OrderList orderList = new OrderList(); + for (JsonAdaptedOrder jsonAdaptedOrder : orders) { + Order order = jsonAdaptedOrder.toModelType(); + if (orderList.hasItem(order)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_ORDER); + } + orderList.addItem(order); + } + return orderList; + } + //@@author +} diff --git a/src/main/java/trackr/storage/JsonTrackrStorage.java b/src/main/java/trackr/storage/JsonTrackrStorage.java new file mode 100644 index 00000000000..8e100c94052 --- /dev/null +++ b/src/main/java/trackr/storage/JsonTrackrStorage.java @@ -0,0 +1,170 @@ +package trackr.storage; + +import static java.util.Objects.requireNonNull; +import static trackr.commons.util.CollectionUtil.requireAllNonNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import trackr.commons.core.LogsCenter; +import trackr.commons.exceptions.DataConversionException; +import trackr.commons.exceptions.IllegalValueException; +import trackr.commons.util.FileUtil; +import trackr.commons.util.JsonUtil; +import trackr.model.ReadOnlyMenu; +import trackr.model.ReadOnlyOrderList; +import trackr.model.ReadOnlySupplierList; +import trackr.model.ReadOnlyTaskList; + +/** + * A class to access Trackr data stored as a json file on the hard disk. + */ +public class JsonTrackrStorage implements TrackrStorage { + //@@author liumc-sg-reused + private static final Logger logger = LogsCenter.getLogger(JsonTrackrStorage.class); + + private Path filePath; + + public JsonTrackrStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getTrackrFilePath() { + return filePath; + } + + @Override + public Optional readSupplierList() throws DataConversionException { + return readSupplierList(filePath); + } + + /** + * Similar to {@link #readSupplierList}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readSupplierList(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonTrackr = JsonUtil.readJsonFile( + filePath, JsonSerializableTrackr.class); + if (!jsonTrackr.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonTrackr.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public Optional readTaskList() throws DataConversionException { + return readTaskList(filePath); + } + + /** + * Similar to {@link #readTaskList}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readTaskList(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonTrackr = JsonUtil.readJsonFile( + filePath, JsonSerializableTrackr.class); + if (!jsonTrackr.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonTrackr.get().toTaskModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + //@@author changgittyhub-reused + @Override + public Optional readMenu() throws DataConversionException { + return readMenu(filePath); + } + + /** + * Similar to {@link #readMenu}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readMenu(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonTrackr = JsonUtil.readJsonFile( + filePath, JsonSerializableTrackr.class); + if (!jsonTrackr.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonTrackr.get().toMenuItemModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + //@@author chongweiguan-reused + @Override + public Optional readOrderList() throws DataConversionException { + return readOrderList(filePath); + } + + /** + * Similar to {@link #readOrderList}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readOrderList(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonTrackr = JsonUtil.readJsonFile( + filePath, JsonSerializableTrackr.class); + if (!jsonTrackr.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonTrackr.get().toOrderModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + //@@author + + @Override + public void saveTrackr(ReadOnlySupplierList addressBook, ReadOnlyTaskList taskList, + ReadOnlyMenu menu, ReadOnlyOrderList orderList) throws IOException { + saveTrackr(addressBook, taskList, menu, orderList, filePath); + } + + //@@author liumc-sg-reused + @Override + public void saveTrackr(ReadOnlySupplierList addressBook, ReadOnlyTaskList taskList, + ReadOnlyMenu menu, ReadOnlyOrderList orderList, Path filePath) + throws IOException { + requireAllNonNull(addressBook, taskList, menu, orderList, filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableTrackr(addressBook, taskList, menu, orderList), filePath); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/trackr/storage/JsonUserPrefsStorage.java similarity index 83% rename from src/main/java/seedu/address/storage/JsonUserPrefsStorage.java rename to src/main/java/trackr/storage/JsonUserPrefsStorage.java index bc2bbad84aa..8e2cdcfd881 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java +++ b/src/main/java/trackr/storage/JsonUserPrefsStorage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package trackr.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import trackr.commons.exceptions.DataConversionException; +import trackr.commons.util.JsonUtil; +import trackr.model.ReadOnlyUserPrefs; +import trackr.model.UserPrefs; /** * A class to access UserPrefs stored in the hard disk as a json file diff --git a/src/main/java/trackr/storage/Storage.java b/src/main/java/trackr/storage/Storage.java new file mode 100644 index 00000000000..68804d21f25 --- /dev/null +++ b/src/main/java/trackr/storage/Storage.java @@ -0,0 +1,44 @@ +package trackr.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import trackr.commons.exceptions.DataConversionException; +import trackr.model.ReadOnlyMenu; +import trackr.model.ReadOnlyOrderList; +import trackr.model.ReadOnlySupplierList; +import trackr.model.ReadOnlyTaskList; +import trackr.model.ReadOnlyUserPrefs; +import trackr.model.UserPrefs; + +/** + * API of the Storage component + */ +public interface Storage extends TrackrStorage, UserPrefsStorage { + + @Override + Optional readUserPrefs() throws DataConversionException, IOException; + + @Override + void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; + + @Override + Path getTrackrFilePath(); + + @Override + Optional readSupplierList() throws DataConversionException, IOException; + + @Override + Optional readTaskList() throws DataConversionException, IOException; + + @Override + Optional readMenu() throws DataConversionException, IOException; + + @Override + Optional readOrderList() throws DataConversionException, IOException; + + @Override + void saveTrackr(ReadOnlySupplierList addressBook, ReadOnlyTaskList taskList, + ReadOnlyMenu menu, ReadOnlyOrderList orderList) throws IOException; +} diff --git a/src/main/java/trackr/storage/StorageManager.java b/src/main/java/trackr/storage/StorageManager.java new file mode 100644 index 00000000000..603e1ff1c59 --- /dev/null +++ b/src/main/java/trackr/storage/StorageManager.java @@ -0,0 +1,128 @@ +package trackr.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import trackr.commons.core.LogsCenter; +import trackr.commons.exceptions.DataConversionException; +import trackr.model.ReadOnlyMenu; +import trackr.model.ReadOnlyOrderList; +import trackr.model.ReadOnlySupplierList; +import trackr.model.ReadOnlyTaskList; +import trackr.model.ReadOnlyUserPrefs; +import trackr.model.UserPrefs; + +/** + * Manages storage of AddressBook data in local storage. + */ +public class StorageManager implements Storage { + + private static final Logger logger = LogsCenter.getLogger(StorageManager.class); + private TrackrStorage trackrStorage; + private UserPrefsStorage userPrefsStorage; + + /** + * Creates a {@code StorageManager} with the given {@code TrackrStorage} and {@code UserPrefStorage}. + */ + //@@author liumc-sg-reused + public StorageManager(TrackrStorage trackrStorage, UserPrefsStorage userPrefsStorage) { + this.trackrStorage = trackrStorage; + this.userPrefsStorage = userPrefsStorage; + } + //@@author + + // ================ UserPrefs methods ============================== + + @Override + public Path getUserPrefsFilePath() { + return userPrefsStorage.getUserPrefsFilePath(); + } + + @Override + public Optional readUserPrefs() throws DataConversionException, IOException { + return userPrefsStorage.readUserPrefs(); + } + + @Override + public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { + userPrefsStorage.saveUserPrefs(userPrefs); + } + + + // ================ Trackr methods ============================== + + //@@author liumc-sg-reused + @Override + public Path getTrackrFilePath() { + return trackrStorage.getTrackrFilePath(); + } + //@@author + + @Override + public Optional readSupplierList() throws DataConversionException, IOException { + return readSupplierList(trackrStorage.getTrackrFilePath()); + } + + @Override + public Optional readSupplierList(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return trackrStorage.readSupplierList(filePath); + } + + //@@author liumc-sg-reused + @Override + public Optional readTaskList() throws DataConversionException, IOException { + return readTaskList(trackrStorage.getTrackrFilePath()); + } + + @Override + public Optional readTaskList(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return trackrStorage.readTaskList(filePath); + } + //@@author + + //@@author changgittyhub-reused + @Override + public Optional readMenu() throws DataConversionException, IOException { + return readMenu(trackrStorage.getTrackrFilePath()); + } + + //@@author changgittyhub-reused + @Override + public Optional readMenu(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return trackrStorage.readMenu(filePath); + } + + //@@author chongweiguan-reused + @Override + public Optional readOrderList() throws DataConversionException, IOException { + return readOrderList(trackrStorage.getTrackrFilePath()); + } + + @Override + public Optional readOrderList(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return trackrStorage.readOrderList(filePath); + } + //@@author + + //@@author liumc-sg-reused + @Override + public void saveTrackr(ReadOnlySupplierList supplierList, ReadOnlyTaskList taskList, + ReadOnlyMenu menu, ReadOnlyOrderList orderList) throws IOException { + saveTrackr(supplierList, taskList, menu, orderList, trackrStorage.getTrackrFilePath()); + } + + @Override + public void saveTrackr(ReadOnlySupplierList supplierList, ReadOnlyTaskList taskList, + ReadOnlyMenu menu, ReadOnlyOrderList orderList, Path filePath) + throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + trackrStorage.saveTrackr(supplierList, taskList, menu, orderList, filePath); + } + +} diff --git a/src/main/java/trackr/storage/TrackrStorage.java b/src/main/java/trackr/storage/TrackrStorage.java new file mode 100644 index 00000000000..50d7d1206b8 --- /dev/null +++ b/src/main/java/trackr/storage/TrackrStorage.java @@ -0,0 +1,94 @@ +package trackr.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import trackr.commons.exceptions.DataConversionException; +import trackr.model.ReadOnlyMenu; +import trackr.model.ReadOnlyOrderList; +import trackr.model.ReadOnlySupplierList; +import trackr.model.ReadOnlyTaskList; + +/** + * Represents a storage for {@link trackr.model.SupplierList} and {@link trackr.model.TaskList}. + */ +public interface TrackrStorage { + + //@@author liumc-sg-reused + /** + * Returns the file path of the data file. + */ + Path getTrackrFilePath(); + + /** + * Returns SupplierList data as a {@link ReadOnlySupplierList}. + * 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 readSupplierList() throws DataConversionException, IOException; + + /** + * @see #getTrackrFilePath() + */ + Optional readSupplierList(Path filePath) throws DataConversionException, IOException; + //@@author + + /** + * Saves the given {@link ReadOnlySupplierList}, {@link ReadOnlyTaskList} + * and {@link ReadOnlyOrderList} to the storage. + * @param addressBook cannot be null. + * @param taskList cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveTrackr(ReadOnlySupplierList addressBook, ReadOnlyTaskList taskList, + ReadOnlyMenu menu, ReadOnlyOrderList orderList) throws IOException; + + /** + * @see #saveTrackr(ReadOnlySupplierList, ReadOnlyTaskList, ReadOnlyMenu, ReadOnlyOrderList) + */ + void saveTrackr(ReadOnlySupplierList addressBook, ReadOnlyTaskList taskList, + ReadOnlyMenu menu, ReadOnlyOrderList orderList, Path filePath) throws IOException; + + /** + * @see #getTrackrFilePath() + */ + Optional readTaskList(Path filePath) throws DataConversionException, IOException; + + /** + * Returns TaskList data as a {@link ReadOnlyTaskList}. + * 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 readTaskList() throws DataConversionException, IOException; + + /** + * @see #getTrackrFilePath() + */ + //@@author changgittyhub-reused + Optional readMenu(Path filePath) throws DataConversionException, IOException; + + /** + * Returns TaskList data as a {@link ReadOnlyMenu}. + * 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. + */ + //@@author changgittyhub-reused + Optional readMenu() throws DataConversionException, IOException; + + /** + * @see #getTrackrFilePath() + */ + Optional readOrderList(Path filePath) throws DataConversionException, IOException; + + /** + * Returns OrderList data as a {@link ReadOnlyOrderList}. + * 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 readOrderList() throws DataConversionException, IOException; +} diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/trackr/storage/UserPrefsStorage.java similarity index 71% rename from src/main/java/seedu/address/storage/UserPrefsStorage.java rename to src/main/java/trackr/storage/UserPrefsStorage.java index 29eef178dbc..752ec8c3ca5 100644 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ b/src/main/java/trackr/storage/UserPrefsStorage.java @@ -1,15 +1,15 @@ -package seedu.address.storage; +package trackr.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import trackr.commons.exceptions.DataConversionException; +import trackr.model.ReadOnlyUserPrefs; +import trackr.model.UserPrefs; /** - * Represents a storage for {@link seedu.address.model.UserPrefs}. + * Represents a storage for {@link trackr.model.UserPrefs}. */ public interface UserPrefsStorage { @@ -27,7 +27,7 @@ public interface UserPrefsStorage { Optional readUserPrefs() throws DataConversionException, IOException; /** - * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage. + * Saves the given {@link trackr.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/trackr/ui/CommandBox.java similarity index 89% rename from src/main/java/seedu/address/ui/CommandBox.java rename to src/main/java/trackr/ui/CommandBox.java index 9e75478664b..fa20ee973b9 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/trackr/ui/CommandBox.java @@ -1,12 +1,12 @@ -package seedu.address.ui; +package trackr.ui; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.TextField; import javafx.scene.layout.Region; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import trackr.logic.commands.CommandResult; +import trackr.logic.commands.exceptions.CommandException; +import trackr.logic.parser.exceptions.ParseException; /** * The UI component that is responsible for receiving user command inputs. @@ -77,7 +77,7 @@ public interface CommandExecutor { /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see trackr.logic.Logic#execute(String) */ CommandResult execute(String commandText) throws CommandException, ParseException; } diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/trackr/ui/HelpWindow.java similarity index 93% rename from src/main/java/seedu/address/ui/HelpWindow.java rename to src/main/java/trackr/ui/HelpWindow.java index 3f16b2fcf26..2b668d27f53 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/trackr/ui/HelpWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package trackr.ui; import java.util.logging.Logger; @@ -8,14 +8,14 @@ import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.stage.Stage; -import seedu.address.commons.core.LogsCenter; +import trackr.commons.core.LogsCenter; /** * Controller for 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 USERGUIDE_URL = "https://ay2223s2-cs2103t-w15-2.github.io/tp/UserGuide.html"; public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/trackr/ui/MainWindow.java similarity index 74% rename from src/main/java/seedu/address/ui/MainWindow.java rename to src/main/java/trackr/ui/MainWindow.java index 9106c3aa6e5..296576c49bc 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/trackr/ui/MainWindow.java @@ -1,5 +1,11 @@ -package seedu.address.ui; +package trackr.ui; +import static trackr.logic.parser.TrackrParser.getModel; +import static trackr.model.TabEnum.getTabIndex; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; import java.util.logging.Logger; import javafx.event.ActionEvent; @@ -9,13 +15,16 @@ import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; import javafx.scene.layout.StackPane; +import javafx.stage.FileChooser; import javafx.stage.Stage; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.logic.Logic; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import trackr.commons.core.GuiSettings; +import trackr.commons.core.LogsCenter; +import trackr.logic.Logic; +import trackr.logic.commands.CommandResult; +import trackr.logic.commands.exceptions.CommandException; +import trackr.logic.parser.exceptions.ParseException; +import trackr.model.ObservableTabIndex; +import trackr.ui.dashboard.TabPanel; /** * The Main Window. Provides the basic application layout containing @@ -31,18 +40,18 @@ public class MainWindow extends UiPart { private Logic logic; // Independent Ui parts residing in this Ui container - private PersonListPanel personListPanel; + private TabPanel tabPanel; private ResultDisplay resultDisplay; private HelpWindow helpWindow; @FXML - private StackPane commandBoxPlaceholder; + private StackPane tabPanelPlaceholder; @FXML - private MenuItem helpMenuItem; + private StackPane commandBoxPlaceholder; @FXML - private StackPane personListPanelPlaceholder; + private MenuItem helpMenuItem; @FXML private StackPane resultDisplayPlaceholder; @@ -110,13 +119,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()); + //@@author arkarsg + tabPanel = new TabPanel(logic); + tabPanelPlaceholder.getChildren().add(tabPanel.getRoot()); resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); - - StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); + //@@author + StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getTrackrFilePath()); statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); CommandBox commandBox = new CommandBox(this::executeCommand); @@ -147,6 +157,33 @@ public void handleHelp() { } } + //@@author chongweiguan-reused + /** + * Executes uploadcsv command + */ + @FXML + public void handleAddCsv() throws Exception { + FileChooser fileChooser = new FileChooser(); + fileChooser.setInitialDirectory(new File(System.getProperty("user.home"))); + File selectedFile = fileChooser.showOpenDialog(new Stage()); + String components = ""; + + if (selectedFile != null) { + FileReader fr = new FileReader(selectedFile); + BufferedReader br = new BufferedReader(fr); + String line = ""; + while ((line = br.readLine()) != null) { + String[] strArr = line.split(","); + for (String str : strArr) { + components = components + str + ","; + } + } + br.close(); + String command = "upload_csv ," + components; + executeCommand(command); + } + } + void show() { primaryStage.show(); } @@ -163,20 +200,19 @@ private void handleExit() { primaryStage.hide(); } - public PersonListPanel getPersonListPanel() { - return personListPanel; - } - /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see trackr.logic.Logic#execute(String) */ private CommandResult executeCommand(String commandText) throws CommandException, ParseException { try { CommandResult commandResult = logic.execute(commandText); logger.info("Result: " + commandResult.getFeedbackToUser()); resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser()); + //@@author arkarsg + ObservableTabIndex.updateToTab(getTabIndex(getModel(commandText))); + //@@author arkarsg if (commandResult.isShowHelp()) { handleHelp(); @@ -185,7 +221,6 @@ private CommandResult executeCommand(String commandText) throws CommandException if (commandResult.isExit()) { handleExit(); } - return commandResult; } catch (CommandException | ParseException e) { logger.info("Invalid command: " + commandText); diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/trackr/ui/ResultDisplay.java similarity index 95% rename from src/main/java/seedu/address/ui/ResultDisplay.java rename to src/main/java/trackr/ui/ResultDisplay.java index 7d98e84eedf..946fb12d6ff 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/trackr/ui/ResultDisplay.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package trackr.ui; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/trackr/ui/StatusBarFooter.java similarity index 96% rename from src/main/java/seedu/address/ui/StatusBarFooter.java rename to src/main/java/trackr/ui/StatusBarFooter.java index b577f829423..954bbedcae6 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/trackr/ui/StatusBarFooter.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package trackr.ui; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/ui/Ui.java b/src/main/java/trackr/ui/Ui.java similarity index 86% rename from src/main/java/seedu/address/ui/Ui.java rename to src/main/java/trackr/ui/Ui.java index 17aa0b494fe..ae6dffa6f62 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/trackr/ui/Ui.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package trackr.ui; import javafx.stage.Stage; diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/trackr/ui/UiManager.java similarity index 94% rename from src/main/java/seedu/address/ui/UiManager.java rename to src/main/java/trackr/ui/UiManager.java index fdf024138bc..1cd5af13aca 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/trackr/ui/UiManager.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package trackr.ui; import java.util.logging.Logger; @@ -7,10 +7,10 @@ import javafx.scene.control.Alert.AlertType; import javafx.scene.image.Image; import javafx.stage.Stage; -import seedu.address.MainApp; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; +import trackr.MainApp; +import trackr.commons.core.LogsCenter; +import trackr.commons.util.StringUtil; +import trackr.logic.Logic; /** * The manager of the UI component. diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/trackr/ui/UiPart.java similarity index 97% rename from src/main/java/seedu/address/ui/UiPart.java rename to src/main/java/trackr/ui/UiPart.java index fc820e01a9c..21517d0bc0b 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/trackr/ui/UiPart.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package trackr.ui; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.net.URL; import javafx.fxml.FXMLLoader; -import seedu.address.MainApp; +import trackr.MainApp; /** * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. diff --git a/src/main/java/trackr/ui/cards/MenuCard.java b/src/main/java/trackr/ui/cards/MenuCard.java new file mode 100644 index 00000000000..95334728852 --- /dev/null +++ b/src/main/java/trackr/ui/cards/MenuCard.java @@ -0,0 +1,72 @@ +package trackr.ui.cards; + + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import trackr.model.menu.MenuItem; +import trackr.ui.UiPart; + +//@@author arkarsg-reused +/** + * An UI component that displays information of a {@code Order}. + */ +public class MenuCard extends UiPart { + + private static final String FXML = "MenuListCard.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 MenuItem menuItem; + + @FXML + private HBox cardPane; + @FXML + private Label id; + @FXML + private Label itemName; + @FXML + private Label itemPrice; + @FXML + private Label itemCost; + @FXML + private Label itemProfit; + + /** + * Creates a {@code MenuCard} with the given {@code MenuItem} and index to display. + */ + public MenuCard(MenuItem menuItem, int displayedIndex) { + super(FXML); + this.menuItem = menuItem; + id.setText(displayedIndex + ". "); + itemName.setText(menuItem.getItemName().getName()); + itemPrice.setText(menuItem.getItemPrice().toString()); + itemCost.setText(menuItem.getItemCost().toString()); + itemProfit.setText(menuItem.getItemProfit().toString()); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof MenuCard)) { + return false; + } + + // state check + MenuCard card = (MenuCard) other; + return id.getText().equals(card.id.getText()) + && menuItem.equals(card.menuItem); + } +} diff --git a/src/main/java/trackr/ui/cards/OrderCard.java b/src/main/java/trackr/ui/cards/OrderCard.java new file mode 100644 index 00000000000..7ab863ed64d --- /dev/null +++ b/src/main/java/trackr/ui/cards/OrderCard.java @@ -0,0 +1,81 @@ +package trackr.ui.cards; + + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import trackr.model.order.Order; +import trackr.ui.UiPart; + +//@@author arkarsg-reused +/** + * An UI component that displays information of a {@code Order}. + */ +public class OrderCard extends UiPart { + + private static final String FXML = "OrderListCard.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 Order order; + + @FXML + private HBox cardPane; + @FXML + private Label id; + @FXML + private Label orderName; + @FXML + private Label orderQuantity; + @FXML + private Label orderDeadline; + @FXML + private Label orderStatus; + @FXML + private Label customerName; + @FXML + private Label customerPhone; + @FXML + private Label customerAddress; + + /** + * Creates a {@code orderCode} with the given {@code Order} and index to display. + */ + public OrderCard(Order order, int displayedIndex) { + super(FXML); + this.order = order; + id.setText(displayedIndex + ". "); + orderName.setText(order.getOrderName().getName()); + orderQuantity.setText(order.getOrderQuantity().toString()); + orderDeadline.setText(order.getOrderDeadline().toString()); + orderStatus.setText(order.getOrderStatus().toString()); + customerName.setText(order.getCustomer().getCustomerName().getName()); + customerAddress.setText(order.getCustomer().getCustomerAddress().personAddress); + customerPhone.setText(order.getCustomer().getCustomerPhone().personPhone); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof OrderCard)) { + return false; + } + + // state check + OrderCard card = (OrderCard) other; + return id.getText().equals(card.id.getText()) + && order.equals(card.order); + } +} diff --git a/src/main/java/trackr/ui/cards/SummarisedOrderCard.java b/src/main/java/trackr/ui/cards/SummarisedOrderCard.java new file mode 100644 index 00000000000..23e8e012faf --- /dev/null +++ b/src/main/java/trackr/ui/cards/SummarisedOrderCard.java @@ -0,0 +1,73 @@ +package trackr.ui.cards; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import trackr.model.order.Order; +import trackr.ui.UiPart; + +//@@author arkarsg-reused +/** + * A UI component that displays summarised information of {@code Order}. + */ +public class SummarisedOrderCard extends UiPart { + private static final String FXML = "SummarisedOrderCard.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 Order order; + + @FXML + private HBox cardPane; + @FXML + private Label id; + @FXML + private Label orderName; + @FXML + private Label orderQuantity; + @FXML + private Label orderDeadline; + @FXML + private Label orderStatus; + @FXML + private Label customerName; + + /** + * Creates a {@code summarisedContactCard} with the given {@code Order} and index + */ + public SummarisedOrderCard(Order order, int displayedIndex) { + super(FXML); + this.order = order; + id.setText(displayedIndex + ". "); + orderName.setText(order.getOrderName().getName()); + orderQuantity.setText(order.getOrderQuantity().toString()); + orderDeadline.setText(order.getOrderDeadline().toString()); + orderStatus.setText(order.getOrderStatus().toString()); + customerName.setText(order.getCustomer().getCustomerName().getName()); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof SummarisedOrderCard)) { + return false; + } + + // state check + SummarisedOrderCard card = (SummarisedOrderCard) other; + return id.getText().equals(card.id.getText()) + && order.equals(card.order); + } +} diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/trackr/ui/cards/SupplierCard.java similarity index 59% rename from src/main/java/seedu/address/ui/PersonCard.java rename to src/main/java/trackr/ui/cards/SupplierCard.java index 7fc927bc5d9..acb2da8b1f4 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/trackr/ui/cards/SupplierCard.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package trackr.ui.cards; import java.util.Comparator; @@ -7,14 +7,16 @@ import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; -import seedu.address.model.person.Person; +import trackr.model.person.Supplier; +import trackr.ui.UiPart; +//@@author arkars-reusedg /** - * An UI component that displays information of a {@code Person}. + * An UI component that displays information of a {@code Supplier}. */ -public class PersonCard extends UiPart { +public class SupplierCard extends UiPart { - private static final String FXML = "PersonListCard.fxml"; + private static final String FXML = "SupplierListCard.fxml"; /** * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. @@ -24,7 +26,7 @@ public class PersonCard extends UiPart { * @see The issue on AddressBook level 4 */ - public final Person person; + public final Supplier supplier; @FXML private HBox cardPane; @@ -42,17 +44,17 @@ public class PersonCard extends UiPart { private FlowPane tags; /** - * Creates a {@code PersonCode} with the given {@code Person} and index to display. + * Creates a {@code supplierCode} with the given {@code Supplier} and index to display. */ - public PersonCard(Person person, int displayedIndex) { + public SupplierCard(Supplier supplier, int displayedIndex) { super(FXML); - this.person = person; + this.supplier = supplier; id.setText(displayedIndex + ". "); - name.setText(person.getName().fullName); - phone.setText(person.getPhone().value); - address.setText(person.getAddress().value); - email.setText(person.getEmail().value); - person.getTags().stream() + name.setText(supplier.getPersonName().getName()); + phone.setText(supplier.getPersonPhone().personPhone); + address.setText(supplier.getPersonAddress().personAddress); + email.setText(supplier.getPersonEmail().personEmail); + supplier.getPersonTags().stream() .sorted(Comparator.comparing(tag -> tag.tagName)) .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); } @@ -65,13 +67,13 @@ public boolean equals(Object other) { } // instanceof handles nulls - if (!(other instanceof PersonCard)) { + if (!(other instanceof SupplierCard)) { return false; } // state check - PersonCard card = (PersonCard) other; + SupplierCard card = (SupplierCard) other; return id.getText().equals(card.id.getText()) - && person.equals(card.person); + && supplier.equals(card.supplier); } } diff --git a/src/main/java/trackr/ui/cards/TaskCard.java b/src/main/java/trackr/ui/cards/TaskCard.java new file mode 100644 index 00000000000..571df315170 --- /dev/null +++ b/src/main/java/trackr/ui/cards/TaskCard.java @@ -0,0 +1,70 @@ +package trackr.ui.cards; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import trackr.model.task.Task; +import trackr.ui.UiPart; + +//@@author HmuuMyatMoe-reused +// Reused from AB3 with minor modifications +/** + * An UI component that displays information of a {@code Task}. + */ +public class TaskCard extends UiPart { + + private static final String FXML = "TaskListCard.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 Task task; + + @FXML + private HBox cardPane; + @FXML + private Label taskName; + @FXML + private Label id; + @FXML + private Label taskDeadline; + @FXML + private Label taskStatus; + + /** + * Creates a {@code TaskCode} with the given {@code Task} and index to display. + */ + public TaskCard(Task task, int displayedIndex) { + super(FXML); + this.task = task; + id.setText(displayedIndex + ". "); + taskName.setText(task.getTaskName().getName()); + taskDeadline.setText(task.getTaskDeadline().toString()); + taskStatus.setText(task.getTaskStatus().toString()); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof TaskCard)) { + return false; + } + + // state check + TaskCard card = (TaskCard) other; + return id.getText().equals(card.id.getText()) + && task.equals(card.task); + } +} +//@@author diff --git a/src/main/java/trackr/ui/dashboard/HomeView.java b/src/main/java/trackr/ui/dashboard/HomeView.java new file mode 100644 index 00000000000..f11b1c680d0 --- /dev/null +++ b/src/main/java/trackr/ui/dashboard/HomeView.java @@ -0,0 +1,86 @@ +package trackr.ui.dashboard; + +import javafx.collections.ListChangeListener; +import javafx.fxml.FXML; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.scene.text.Text; +import trackr.logic.Logic; +import trackr.model.order.Order; +import trackr.ui.UiPart; +import trackr.ui.listpanels.MenuListPanel; +import trackr.ui.listpanels.SummarisedOrderListPanel; +import trackr.ui.listpanels.TaskListPanel; + +//@@author arkarsg +/** + * The UI component that is resposnible for displaying Home tab. + */ +public class HomeView extends UiPart { + private static final String FXML = "HomeDashboard.fxml"; + private Logic logic; + private TaskListPanel taskListPanel; + private SummarisedOrderListPanel summarisedOrderListPanel; + private MenuListPanel menuListPanel; + + @FXML + private Text profitPlaceholder; + + @FXML + private Text revenuePlaceholder; + + @FXML + private GridPane homeWindow; + + @FXML + private StackPane tasksPlaceholder; + + @FXML + private StackPane ordersPlaceholder; + + @FXML + private StackPane menuPlaceholder; + + /** + * Creates the landing page + */ + public HomeView(Logic logic) { + super(FXML); + this.logic = logic; + fillParts(); + } + + /** + * The Landing page consists of a 4x4 GridPane with elements that span + * different rows + */ + public void fillParts() { + homeWindow = new GridPane(); + // Add tasks + taskListPanel = new TaskListPanel(logic.getFilteredTaskList()); + tasksPlaceholder.getChildren().add(taskListPanel.getRoot()); + + // Add summarised orders + summarisedOrderListPanel = new SummarisedOrderListPanel(logic.getFilteredOrderList()); + ordersPlaceholder.getChildren().add(summarisedOrderListPanel.getRoot()); + + // Add menu items + menuListPanel = new MenuListPanel(logic.getFilteredMenu()); + menuPlaceholder.getChildren().add(menuListPanel.getRoot()); + + // Initial Profit + profitPlaceholder.setText(logic.getTotalProfits().toString()); + // Initial Revenue + revenuePlaceholder.setText(logic.getTotalSales().toString()); + + // event change listener to update revenue and profit + logic.getFilteredOrderList().addListener(new ListChangeListener() { + @Override + public void onChanged(ListChangeListener.Change change) { + profitPlaceholder.setText(logic.getTotalProfits().toString()); + revenuePlaceholder.setText(logic.getTotalSales().toString()); + } + }); + } +} diff --git a/src/main/java/trackr/ui/dashboard/TabPanel.java b/src/main/java/trackr/ui/dashboard/TabPanel.java new file mode 100644 index 00000000000..c24ead8c63f --- /dev/null +++ b/src/main/java/trackr/ui/dashboard/TabPanel.java @@ -0,0 +1,138 @@ +package trackr.ui.dashboard; + +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.fxml.FXML; +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import trackr.logic.Logic; +import trackr.model.ObservableTabIndex; +import trackr.ui.UiPart; +import trackr.ui.listpanels.MenuListPanel; +import trackr.ui.listpanels.OrderListPanel; +import trackr.ui.listpanels.SupplierListPanel; +import trackr.ui.listpanels.TaskListPanel; + +//@@author arkarsg +/** + * The UI component that is responsible for displaying Tab views. + */ +public class TabPanel extends UiPart { + private static final String FXML = "TabPanel.fxml"; + + private Logic logic; + + private HomeView homeView; + private SupplierListPanel contactListPanel; + private TaskListPanel taskListPanel; + private OrderListPanel orderListPanel; + private MenuListPanel menuListPanel; + + @FXML + private TabPane tabPanel; + + @FXML + private Tab homeTab; + + @FXML + private Tab orderTab; + + @FXML + private Tab taskTab; + + @FXML + private Tab contactTab; + + @FXML + private Tab menuTab; + + @FXML + private StackPane homePanelPlaceholder; + + @FXML + private StackPane taskListPanelPlaceholder; + + @FXML + private StackPane orderListPanelPlaceholder; + + @FXML + private StackPane contactListPanelPlaceholder; + + @FXML + private StackPane menuListPanelPlaceholder; + + /** + * Creates an empty TabPane + */ + public TabPanel(Logic logic) { + super(FXML); + this.logic = logic; + fillTabs(); + ObservableTabIndex.valueProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue o, Number oldIdx, Number newIdx) { + switchToTab(newIdx.intValue()); + } + }); + } + + /** + * Adds all tab in order. + */ + private void fillTabs() { + homeView = new HomeView(logic); + homePanelPlaceholder.getChildren().add(homeView.getRoot()); + fillNewTab(homeTab, homePanelPlaceholder, "Home"); + + orderListPanel = new OrderListPanel(logic.getFilteredOrderList()); + orderListPanelPlaceholder + .getChildren() + .add(orderListPanel.getRoot()); + fillNewTab(orderTab, orderListPanelPlaceholder, "Orders"); + + taskListPanel = new TaskListPanel(logic.getFilteredTaskList()); + taskListPanelPlaceholder + .getChildren() + .add(taskListPanel.getRoot()); + fillNewTab(taskTab, taskListPanelPlaceholder, "Tasks"); + + contactListPanel = new SupplierListPanel(logic.getFilteredSupplierList()); + contactListPanelPlaceholder + .getChildren() + .add(contactListPanel.getRoot()); + fillNewTab(contactTab, contactListPanelPlaceholder, "Contacts"); + + // Create placeholder with text for menu + menuListPanel = new MenuListPanel(logic.getFilteredMenu()); + menuListPanelPlaceholder + .getChildren() + .add(menuListPanel.getRoot()); + fillNewTab(menuTab, menuListPanelPlaceholder, "Menu"); + } + + /** + * Adds a {@code tab} with the given {@code tabContent} and {@code tabName} + */ + private void fillNewTab(Tab tab, StackPane tabContent, String tabName) { + tab.setText(tabName); + tab.setContent(tabContent); + } + + public void switchToTab(int index) { + tabPanel.getSelectionModel().select(index); + } + + public SupplierListPanel getContactListPanel() { + return contactListPanel; + } + + public TaskListPanel getTaskListPanel() { + return taskListPanel; + } + + public OrderListPanel getOrderListPanel() { + return orderListPanel; + } +} diff --git a/src/main/java/trackr/ui/listpanels/MenuListPanel.java b/src/main/java/trackr/ui/listpanels/MenuListPanel.java new file mode 100644 index 00000000000..e36edfe14bf --- /dev/null +++ b/src/main/java/trackr/ui/listpanels/MenuListPanel.java @@ -0,0 +1,52 @@ +package trackr.ui.listpanels; + +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 trackr.commons.core.LogsCenter; +import trackr.model.menu.MenuItem; +import trackr.ui.UiPart; +import trackr.ui.cards.MenuCard; + +//@@author arkarsg-reused +/** + * Panel containing the list of suppliers. + */ +public class MenuListPanel extends UiPart { + private static final String FXML = "MenuListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(SupplierListPanel.class); + + @FXML + private ListView menuListView; + + /** + * Creates a {@code MenuListPanel} with the given {@code ObservableList}. + */ + public MenuListPanel(ObservableList menuList) { + super(FXML); + menuListView.setItems(menuList); + menuListView.setCellFactory(listView -> new MenuListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Menu} using a {@code MenuCard}. + */ + class MenuListViewCell extends ListCell { + @Override + protected void updateItem(MenuItem menuItem, boolean empty) { + super.updateItem(menuItem, empty); + + if (empty || menuItem == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new MenuCard(menuItem, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/trackr/ui/listpanels/OrderListPanel.java b/src/main/java/trackr/ui/listpanels/OrderListPanel.java new file mode 100644 index 00000000000..fb8d71193b7 --- /dev/null +++ b/src/main/java/trackr/ui/listpanels/OrderListPanel.java @@ -0,0 +1,52 @@ +package trackr.ui.listpanels; + +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 trackr.commons.core.LogsCenter; +import trackr.model.order.Order; +import trackr.ui.UiPart; +import trackr.ui.cards.OrderCard; + +//@@author arkarsg-reused +/** + * Panel containing the list of orders. + */ +public class OrderListPanel extends UiPart { + private static final String FXML = "OrderListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(OrderListPanel.class); + + @FXML + private ListView orderListView; + + /** + * Creates a {@code OrderListPanel} with the given {@code ObservableList}. + */ + public OrderListPanel(ObservableList orderList) { + super(FXML); + orderListView.setItems(orderList); + orderListView.setCellFactory(listView -> new OrderListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Order} using a {@code OrderCard}. + */ + class OrderListViewCell extends ListCell { + @Override + protected void updateItem(Order order, boolean empty) { + super.updateItem(order, empty); + + if (empty || order == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new OrderCard(order, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/trackr/ui/listpanels/SummarisedOrderListPanel.java b/src/main/java/trackr/ui/listpanels/SummarisedOrderListPanel.java new file mode 100644 index 00000000000..d9daa63f56d --- /dev/null +++ b/src/main/java/trackr/ui/listpanels/SummarisedOrderListPanel.java @@ -0,0 +1,51 @@ +package trackr.ui.listpanels; + +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 trackr.commons.core.LogsCenter; +import trackr.model.order.Order; +import trackr.ui.UiPart; +import trackr.ui.cards.SummarisedOrderCard; + +//@@author arkarsg-reused +/** + * Panel containing the list of summarised orders + */ +public class SummarisedOrderListPanel extends UiPart { + private static final String FXML = "SummarisedOrderListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(OrderListPanel.class); + + @FXML + private ListView summarisedOrderListView; + + /** + * Creates a {@code SummarisedOrderListPanel} with the given {@code ObservableList}. + */ + public SummarisedOrderListPanel(ObservableList orderList) { + super(FXML); + summarisedOrderListView.setItems(orderList); + summarisedOrderListView.setCellFactory(listView -> new SummarisedOrderListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Order} using a {@code SummarisedOrderCard} + */ + class SummarisedOrderListViewCell extends ListCell { + @Override + protected void updateItem(Order order, boolean empty) { + super.updateItem(order, empty); + + if (empty || order == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new SummarisedOrderCard(order, getIndex() + 1).getRoot()); + } + } + } +} diff --git a/src/main/java/trackr/ui/listpanels/SupplierListPanel.java b/src/main/java/trackr/ui/listpanels/SupplierListPanel.java new file mode 100644 index 00000000000..62853427e67 --- /dev/null +++ b/src/main/java/trackr/ui/listpanels/SupplierListPanel.java @@ -0,0 +1,52 @@ +package trackr.ui.listpanels; + +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 trackr.commons.core.LogsCenter; +import trackr.model.person.Supplier; +import trackr.ui.UiPart; +import trackr.ui.cards.SupplierCard; + +//@@author arkarsg-reused +/** + * Panel containing the list of suppliers. + */ +public class SupplierListPanel extends UiPart { + private static final String FXML = "SupplierListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(SupplierListPanel.class); + + @FXML + private ListView supplierListView; + + /** + * Creates a {@code SupplierListPanel} with the given {@code ObservableList}. + */ + public SupplierListPanel(ObservableList supplierList) { + super(FXML); + supplierListView.setItems(supplierList); + supplierListView.setCellFactory(listView -> new SupplierListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Supplier} using a {@code SupplierCard}. + */ + class SupplierListViewCell extends ListCell { + @Override + protected void updateItem(Supplier supplier, boolean empty) { + super.updateItem(supplier, empty); + + if (empty || supplier == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new SupplierCard(supplier, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/trackr/ui/listpanels/TaskListPanel.java b/src/main/java/trackr/ui/listpanels/TaskListPanel.java new file mode 100644 index 00000000000..96a2f063a44 --- /dev/null +++ b/src/main/java/trackr/ui/listpanels/TaskListPanel.java @@ -0,0 +1,54 @@ +package trackr.ui.listpanels; + +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 trackr.commons.core.LogsCenter; +import trackr.model.task.Task; +import trackr.ui.UiPart; +import trackr.ui.cards.TaskCard; + +//@@author HmuuMyatMoe-reused +// Reused from AB3 with minor modifications +/** + * Panel containing the list of tasks. + */ +public class TaskListPanel extends UiPart { + private static final String FXML = "TaskListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(TaskListPanel.class); + + @FXML + private ListView taskListView; + + /** + * Creates a {@code TaskListPanel} with the given {@code ObservableList}. + */ + public TaskListPanel(ObservableList taskList) { + super(FXML); + taskListView.setItems(taskList); + taskListView.setCellFactory(listView -> new TaskListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Task} using a {@code TaskCard}. + */ + class TaskListViewCell extends ListCell { + @Override + protected void updateItem(Task task, boolean empty) { + super.updateItem(task, empty); + + if (empty || task == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new TaskCard(task, getIndex() + 1).getRoot()); + } + } + } + +} +//@@author diff --git a/src/main/resources/images/address_book_32.png b/src/main/resources/images/address_book_32.png index 29810cf1fd9..678d27c61a9 100644 Binary files a/src/main/resources/images/address_book_32.png and b/src/main/resources/images/address_book_32.png differ diff --git a/src/main/resources/view/CommandBox.fxml b/src/main/resources/view/CommandBox.fxml index 09f6d6fe9e4..f312c73fa24 100644 --- a/src/main/resources/view/CommandBox.fxml +++ b/src/main/resources/view/CommandBox.fxml @@ -3,7 +3,7 @@ - + diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 36e6b001cd8..710eec256c9 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -1,26 +1,26 @@ .background { - -fx-background-color: derive(#1d1d1d, 20%); - background-color: #383838; /* Used in the default.html file */ + -fx-background-color: derive(#f9f9f9, 20%); + background-color: #f9f9f9; /* Used in the default.html file */ } .label { -fx-font-size: 11pt; -fx-font-family: "Segoe UI Semibold"; - -fx-text-fill: #555555; + -fx-text-fill: #1f1f1f; -fx-opacity: 0.9; } .label-bright { -fx-font-size: 11pt; -fx-font-family: "Segoe UI Semibold"; - -fx-text-fill: white; + -fx-text-fill: #1f1f1f; -fx-opacity: 1; } .label-header { -fx-font-size: 32pt; -fx-font-family: "Segoe UI Light"; - -fx-text-fill: white; + -fx-text-fill: #1f1f1f; -fx-opacity: 1; } @@ -29,182 +29,320 @@ -fx-font-family: "Segoe UI Semibold"; } -.tab-pane { - -fx-padding: 0 0 0 1; -} +/*-----------------------------------------------------------------------------------------------*\ +pane-with-border -.tab-pane .tab-header-area { - -fx-padding: 0 0 0 0; - -fx-min-height: 0; - -fx-max-height: 0; +Styles VBox and StackPane that contains elements in the MainWindow +\*-----------------------------------------------------------------------------------------------*/ +.pane-with-border { + -fx-background-color: derive(#f9f9f9, 20%); } -.table-view { - -fx-base: #1d1d1d; - -fx-control-inner-background: #1d1d1d; - -fx-background-color: #1d1d1d; - -fx-table-cell-border-color: transparent; - -fx-table-header-border-color: transparent; +/*-----------------------------------------------------------------------------------------------*\ +dashboard + +Styles StackPane in TabPanel +\*-----------------------------------------------------------------------------------------------*/ +.dashboard-elems { + -fx-background-radius: 20; -fx-padding: 5; + -fx-border-insets: 5; + -fx-background-insets: 5; + -fx-effect: dropshadow(gaussian, #9999990f, 10, 0.5, 0.0, 0.0); } -.table-view .column-header-background { - -fx-background-color: transparent; +/*-----------------------------------------------------------------------------------------------*\ +TabPane + +Styles TabPane that contains ListView of tasks and suppliers +\*-----------------------------------------------------------------------------------------------*/ + + +.tab-pane > .tab-content-area > * { + -fx-padding: 10 3 3 3; } -.table-view .column-header, .table-view .filler { - -fx-size: 35; - -fx-border-width: 0 0 1 0; +/** +* TabPane Headers +* +* Modifies tab header region to blend in with the background by matching background color +*/ +.tab-header-area .tab-header-background { + -fx-min-height: 10; -fx-background-color: transparent; - -fx-border-color: - transparent - transparent - derive(-fx-base, 80%) - transparent; - -fx-border-insets: 0 10 1 0; } -.table-view .column-header .label { - -fx-font-size: 20pt; - -fx-font-family: "Segoe UI Light"; - -fx-text-fill: white; - -fx-alignment: center-left; - -fx-opacity: 1; +/** +* Modifies tabs and its states +*/ +.tab { + -fx-background: #e1e1e1; + -fx-background-color: -fx-background; + -fx-border-radius: 10 10 10 10; + -fx-background-radius: 10; + -fx-focus-color: transparent; } -.table-view:focused .table-row-cell:filled:focused:selected { - -fx-background-color: -fx-focus-color; +.tab:hover { + -fx-background: #cae1ff; } -.split-pane:horizontal .split-pane-divider { - -fx-background-color: derive(#1d1d1d, 20%); - -fx-border-color: transparent transparent transparent #4d4d4d; +.tab:selected { + -fx-background: #2eabff; } -.split-pane { - -fx-border-radius: 1; - -fx-border-width: 1; - -fx-background-color: derive(#1d1d1d, 20%); -} +/*-----------------------------------------------------------------------------------------------*\ +ListView +Styles SupplierListPanel and TaskListPanel contained by ListView +ListView contains `cells`. Contents of `cells` are populated from `{Object}Card` +\*-----------------------------------------------------------------------------------------------*/ .list-view { -fx-background-insets: 0; - -fx-padding: 0; - -fx-background-color: derive(#1d1d1d, 20%); + -fx-padding: 5; + -fx-border-insets: 5; + -fx-background-color: derive(#f9f9f9, 100%); + -fx-background-radius: 20; + -fx-effect: dropshadow(gaussian, #9999994d, 10, 0.5, 0.0, 0.0); } .list-cell { - -fx-label-padding: 0 0 0 0; - -fx-graphic-text-gap : 0; - -fx-padding: 0 0 0 0; + -fx-border-radius: 20; + -fx-background-radius: 20; + -fx-padding: 3; } +.list-cell:empty { + -fx-background-color: derive(#f9f9f9, 100%); +} .list-cell:filled:even { - -fx-background-color: #3c3e3f; + -fx-background-color: #f9f9f9; + -fx-padding: 3; } .list-cell:filled:odd { - -fx-background-color: #515658; + -fx-background-color: #e1e1e1; + -fx-padding: 3; } -.list-cell:filled:selected { - -fx-background-color: #424d5f; -} - -.list-cell:filled:selected #cardPane { - -fx-border-color: #3e7b91; - -fx-border-width: 1; +.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:selected { + -fx-background-color: #6bc4ff; + -fx-border-radius: 20; + -fx-padding: 3; } .list-cell .label { - -fx-text-fill: white; + -fx-text-fill: #1f1f1f; } +/* Styles index and name */ .cell_big_label { - -fx-font-family: "Segoe UI Semibold"; + -fx-font-family: "Segoe UI"; -fx-font-size: 16px; - -fx-text-fill: #010504; + -fx-text-fill: #1f1f1f; } +/* Styles fields */ .cell_small_label { -fx-font-family: "Segoe UI"; -fx-font-size: 13px; - -fx-text-fill: #010504; + -fx-text-fill: #1f1f1f; } -.stack-pane { - -fx-background-color: derive(#1d1d1d, 20%); +/*-----------------------------------------------------------------------------------------------*\ +Cards + +Styles SupplierCard and TaskCard +\*-----------------------------------------------------------------------------------------------*/ +#cardPane { + -fx-background-color: transparent; + -fx-border-width: 0; } -.pane-with-border { - -fx-background-color: derive(#1d1d1d, 20%); - -fx-border-color: derive(#1d1d1d, 10%); - -fx-border-top-width: 1px; +#commandTypeLabel { + -fx-font-size: 11px; + -fx-text-fill: #6bc4ff; } -.status-bar { - -fx-background-color: derive(#1d1d1d, 30%); + +#tags { + -fx-hgap: 7; + -fx-vgap: 5; } -.result-display { +#tags .label { + -fx-text-fill: rgb(0, 0, 0); + -fx-background-color: #cae1ff; + -fx-padding: 1 3 1 3; + -fx-border-radius: 3; + -fx-background-radius: 3; + -fx-font-size: 11; +} + +#taskField { + -fx-hgap: 7; + -fx-vgap: 5; +} + +#taskField .label { + -fx-text-fill: rgb(0, 0, 0); + -fx-background-color: #cae1ff; + -fx-padding: 1 3 1 3; + -fx-border-radius: 3; + -fx-background-radius: 3; + -fx-font-size: 11; +} + +#orderField { + -fx-hgap: 7; + -fx-vgap: 5; +} + +#orderField .label { + -fx-text-fill: rgb(0, 0, 0); + -fx-background-color: #cae1ff; + -fx-padding: 1 3 1 3; + -fx-border-radius: 3; + -fx-background-radius: 3; + -fx-font-size: 11; +} + +#itemProfit { + -fx-text-fill: rgb(0, 0, 0); + -fx-background-color: #a0ffa4; + -fx-padding: 1 3 1 3; + -fx-border-radius: 3; + -fx-background-radius: 3; + -fx-font-size: 13; +} + +/*-----------------------------------------------------------------------------------------------*\ +scroll-bar + +Styles the scroll bar visible. With JavaFX, ListView is automatically scrollable +\*-----------------------------------------------------------------------------------------------*/ +.scroll-bar { + -fx-background-color: derive(#f9f9f9, 20%); +} + +.scroll-bar .thumb { + -fx-background-color: derive(#6f6f6f, 50%); + -fx-background-insets: 3; +} + +.scroll-bar .increment-button, .scroll-bar .decrement-button { -fx-background-color: transparent; - -fx-font-family: "Segoe UI Light"; - -fx-font-size: 13pt; - -fx-text-fill: white; + -fx-padding: 0 0 0 0; } -.result-display .label { - -fx-text-fill: black !important; +.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow { + -fx-shape: " "; +} + +.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow { + -fx-padding: 1 8 1 8; +} + +.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow { + -fx-padding: 8 1 8 1; +} + +/*-----------------------------------------------------------------------------------------------*\ +status-bar + +Styles the StatusBarFooter present at the bottom of the window +\*-----------------------------------------------------------------------------------------------*/ +.status-bar { + -fx-background-color: derive(#6bc4ff, 30%); } .status-bar .label { - -fx-font-family: "Segoe UI Light"; - -fx-text-fill: white; + -fx-font-family: "Segoe UI"; + -fx-text-fill: #1f1f1f; -fx-padding: 4px; -fx-pref-height: 30px; } .status-bar-with-border { - -fx-background-color: derive(#1d1d1d, 30%); - -fx-border-color: derive(#1d1d1d, 25%); + -fx-background-color: derive(#1f1f1f, 30%); + -fx-border-color: derive(#1f1f1f, 25%); -fx-border-width: 1px; } .status-bar-with-border .label { - -fx-text-fill: white; + -fx-text-fill: #1f1f1f; } -.grid-pane { - -fx-background-color: derive(#1d1d1d, 30%); - -fx-border-color: derive(#1d1d1d, 30%); - -fx-border-width: 1px; +/*-----------------------------------------------------------------------------------------------*\ +result-display + +Styles the ResultDisplay encapsulated by StackPane +\*-----------------------------------------------------------------------------------------------*/ +.result-display { + -fx-background-color: transparent; + -fx-font-family: "Segoe UI"; + -fx-font-size: 13pt; + -fx-text-fill: #1f1f1f; +} + +.result-display .label { + -fx-text-fill: #1f1f1f !important; } -.grid-pane .stack-pane { - -fx-background-color: derive(#1d1d1d, 30%); +#resultDisplay .content { + -fx-background-color: derive(#e1e1e1, 20%); + -fx-background-radius: 10; } -.context-menu { - -fx-background-color: derive(#1d1d1d, 50%); +/*-----------------------------------------------------------------------------------------------*\ +commandTextField + +Styles the user input in the CommandBox +Invalid Command highlighted by `CommandBox::handleCommandEntered` with `Extensions.css` +\*-----------------------------------------------------------------------------------------------*/ +.stack-pane { + -fx-background-color: derive(#e1e1e1, 20%); + -fx-background-radius: 20; + -fx-border-radius: 20; } -.context-menu .label { - -fx-text-fill: white; +#commandTextField { + -fx-background-color: transparent #383838 transparent #383838; + -fx-background-insets: 0; + -fx-border-insets: 0; + -fx-border-width: 0; + -fx-font-family: "Segoe UI"; + -fx-font-size: 13pt; + -fx-text-fill: #1f1f1f; } +/*-----------------------------------------------------------------------------------------------*\ +menu-bar + +Styles the menu bar and dropdown encapsulated by MenuItem in MainWindow +\*-----------------------------------------------------------------------------------------------*/ .menu-bar { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-border-width: 1px; + -fx-border-color: derive(#e3e3e3, 20%); + -fx-background-color: derive(#e3e3e3, 20%); } .menu-bar .label { - -fx-font-size: 14pt; - -fx-font-family: "Segoe UI Light"; - -fx-text-fill: white; + -fx-font-size: 11pt; + -fx-font-family: "Segoe UI"; + -fx-text-fill: #1f1f1f; -fx-opacity: 0.9; } -.menu .left-container { - -fx-background-color: black; +/* +* Styles the dropdown menu +*/ +.context-menu { + -fx-background-color: derive(#e3e3e3, 50%); +} + +.context-menu .label { + -fx-text-fill: #1f1f1f; } /* @@ -230,7 +368,7 @@ .button:pressed, .button:default:hover:pressed { -fx-background-color: white; - -fx-text-fill: #1d1d1d; + -fx-text-fill: #1f1f1f; } .button:focused { @@ -255,98 +393,3 @@ .button:default:hover { -fx-background-color: derive(-fx-focus-color, 30%); } - -.dialog-pane { - -fx-background-color: #1d1d1d; -} - -.dialog-pane > *.button-bar > *.container { - -fx-background-color: #1d1d1d; -} - -.dialog-pane > *.label.content { - -fx-font-size: 14px; - -fx-font-weight: bold; - -fx-text-fill: white; -} - -.dialog-pane:header *.header-panel { - -fx-background-color: derive(#1d1d1d, 25%); -} - -.dialog-pane:header *.header-panel *.label { - -fx-font-size: 18px; - -fx-font-style: italic; - -fx-fill: white; - -fx-text-fill: white; -} - -.scroll-bar { - -fx-background-color: derive(#1d1d1d, 20%); -} - -.scroll-bar .thumb { - -fx-background-color: derive(#1d1d1d, 50%); - -fx-background-insets: 3; -} - -.scroll-bar .increment-button, .scroll-bar .decrement-button { - -fx-background-color: transparent; - -fx-padding: 0 0 0 0; -} - -.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow { - -fx-shape: " "; -} - -.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow { - -fx-padding: 1 8 1 8; -} - -.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow { - -fx-padding: 8 1 8 1; -} - -#cardPane { - -fx-background-color: transparent; - -fx-border-width: 0; -} - -#commandTypeLabel { - -fx-font-size: 11px; - -fx-text-fill: #F70D1A; -} - -#commandTextField { - -fx-background-color: transparent #383838 transparent #383838; - -fx-background-insets: 0; - -fx-border-color: #383838 #383838 #ffffff #383838; - -fx-border-insets: 0; - -fx-border-width: 1; - -fx-font-family: "Segoe UI Light"; - -fx-font-size: 13pt; - -fx-text-fill: white; -} - -#filterField, #personListPanel, #personWebpage { - -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); -} - -#resultDisplay .content { - -fx-background-color: transparent, #383838, transparent, #383838; - -fx-background-radius: 0; -} - -#tags { - -fx-hgap: 7; - -fx-vgap: 3; -} - -#tags .label { - -fx-text-fill: white; - -fx-background-color: #3e7b91; - -fx-padding: 1 3 1 3; - -fx-border-radius: 2; - -fx-background-radius: 2; - -fx-font-size: 11; -} diff --git a/src/main/resources/view/Extensions.css b/src/main/resources/view/Extensions.css index bfe82a85964..e8e058a5683 100644 --- a/src/main/resources/view/Extensions.css +++ b/src/main/resources/view/Extensions.css @@ -5,7 +5,7 @@ .list-cell:empty { /* Empty cells will not have alternating colours */ - -fx-background: #383838; + -fx-background: #f9f9f9; } .tag-selector { diff --git a/src/main/resources/view/HelpWindow.css b/src/main/resources/view/HelpWindow.css index 17e8a8722cd..2f488eee9b0 100644 --- a/src/main/resources/view/HelpWindow.css +++ b/src/main/resources/view/HelpWindow.css @@ -1,19 +1,19 @@ #copyButton, #helpMessage { - -fx-text-fill: white; + -fx-text-fill: #1f1f1f; } #copyButton { - -fx-background-color: dimgray; + -fx-background-color: #6bc4ff; } #copyButton:hover { - -fx-background-color: gray; + -fx-background-color: #2eabff; } #copyButton:armed { - -fx-background-color: darkgray; + -fx-background-color: #2e9aff; } #helpMessageContainer { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#f9f9f9, 20%); } diff --git a/src/main/resources/view/HomeDashboard.fxml b/src/main/resources/view/HomeDashboard.fxml new file mode 100644 index 00000000000..d90fbb3805f --- /dev/null +++ b/src/main/resources/view/HomeDashboard.fxml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index a431648f6c0..cd31b28a51a 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -6,13 +6,15 @@ - + + + + - + @@ -27,32 +29,30 @@

+ - + - + - + - + - + - + - - - + diff --git a/src/main/resources/view/MenuListCard.fxml b/src/main/resources/view/MenuListCard.fxml new file mode 100644 index 00000000000..72158110da6 --- /dev/null +++ b/src/main/resources/view/MenuListCard.fxml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MenuListPanel.fxml b/src/main/resources/view/MenuListPanel.fxml new file mode 100644 index 00000000000..d460db2a67c --- /dev/null +++ b/src/main/resources/view/MenuListPanel.fxml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/OrderListCard.fxml b/src/main/resources/view/OrderListCard.fxml new file mode 100644 index 00000000000..cde56bfb173 --- /dev/null +++ b/src/main/resources/view/OrderListCard.fxml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/OrderListPanel.fxml b/src/main/resources/view/OrderListPanel.fxml new file mode 100644 index 00000000000..2fcfb107f81 --- /dev/null +++ b/src/main/resources/view/OrderListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/PersonListPanel.fxml deleted file mode 100644 index 8836d323cc5..00000000000 --- a/src/main/resources/view/PersonListPanel.fxml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml index 58d5ad3dc56..b58628a4dfb 100644 --- a/src/main/resources/view/ResultDisplay.fxml +++ b/src/main/resources/view/ResultDisplay.fxml @@ -3,7 +3,6 @@ - -