diff --git a/build.gradle b/build.gradle
index 47fbca250ec..c3b51dae38d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -57,6 +57,8 @@ dependencies {
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac'
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux'
+ implementation 'org.apache.commons:commons-lang3:3.12.0'
+
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.7.0'
implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.7.4'
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 5d4d8535457..81b32d1cb00 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -72,7 +72,7 @@ The **API** of this component is specified in [`Ui.java`](https://github.com/se-
![Structure of the UI Component](images/UiClassDiagram.png)
-The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI.
+The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` `HelpWindow` `ViewWindow` 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)
@@ -155,6 +155,62 @@ Classes used by multiple components are in the `seedu.addressbook.commons` packa
This section describes some noteworthy details on how certain features are implemented.
+### Filter Command
+
+#### Implementation
+
+The Address book filter is made with `GradeSubjectFilterPredicate`. It extends from `Predicate` functional interface and is used to set as a condition to check that for each student, the `Grade` and `Subject` specified is met. Predicates created can be updated using the method `Model#updateFilteredPersonList(predicate)`.
+
+The `Grade` and `Subject` classes are set as additional data fields in `Person`.
+
+* Both classes have exposed methods `isEmpty()`, to check if the grade and subject parameters are specified.
+* When unspecified:
+ * `Grade()` equals to `Grade("-")` (i.e. "-" will be shown when there is no grade for the student.)
+ * `Subject()` equals to `Subject("No subject")` (i.e. "No subject" will be shown when no subject is assigned for the student.)
+
+Given below is an example usage scenario and what the predicate is at each step.
+
+Step 1. The user launches the application for the first time. The student's contacts in a form of `FilteredList` will be shown, where the predicate states that condition is true for all.
+
+
:information_source: **Note:** By default, `FilteredList` predicate is set to `FilteredList.ALWAYS_TRUE`. But since both are equivalent, `Model.PREDICATE_SHOW_ALL_PERSONS` is used.
+
+
+
+![FilterState0](images/FilterState0-FilteredList.png)
+
+Step 2. The user executes `filter g/A` command to get all students in the `FilteredList` who has an "A" grade. The `filter` command creates `GradeSubjectFilterPredicate`, and calls `Model#updateFilteredPersonList(predicate)`, updating the list to show students that has an "A" grade.
+
+![FilterState1](images/FilterState1-FilteredList.png)
+
+Calling `list` command will revert the predicate back to `Model.PREDICATE_SHOW_ALL_PERSONS`.
+
+
+
+The following sequence diagram shows how a filter operation goes through the `Logic` component:
+
+![FilterSequenceDiagram-Logic](images/FilterSequenceDiagram-Logic.png)
+
+
:information_source: **Note:** The lifeline for `FilterCommand` and `GradeSubjectFilterPredicate` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+
+
+
+Similarly, how a filter operation goes through the `Model` component is shown below:
+
+![FilterSequenceDiagram-Model](images/FilterSequenceDiagram-Model.png)
+
+The following activity diagram summarizes what happens when a tutor executes a filter command.
+
+![FilterActivityDiagram](images/FilterActivityDiagram.png)
+
+#### Design considerations
+
+* Alternative 1: Filtered address book result can be saved, since in practice, there will only be a few combinations of filters.
+ * Pros: Operation will be fast as the number of students increases.
+ * Cons: More memory usage.
+* Alternative 2: Introduce command history to avoid typing long commands.
+ * Pros: Useful for the entire application, and would use less memory (e.g. storing the first 10 commands).
+ * Cons: Harder to implement.
+
### \[Proposed\] Undo/redo feature
#### Proposed Implementation
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index 192d87a0683..835d86c5acd 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -3,7 +3,7 @@ layout: page
title: User Guide
---
-TutorsGo 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.
+TutorsGo 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, TutorsGo can get your contact management tasks done faster than traditional GUI apps.
* Table of Contents
{:toc}
@@ -14,11 +14,11 @@ TutorsGo is a **desktop app for managing contacts, optimized for use via a Comma
1. Ensure you have Java `11` or above installed in your Computer.
-1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases).
+1. Download the latest `.jar` from [here](https://github.com/AY2324S2-CS2103-F15-2/tp/releases).
1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook.
-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.
+1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar .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)
@@ -27,7 +27,7 @@ TutorsGo is a **desktop app for managing contacts, optimized for use via a Comma
* `list` : Lists all contacts.
- * `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.
+ * `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01 d/2024-02-03 1800 at/present pa/paid` : Adds a contact named `John Doe` to the Address Book.
* `delete 3` : Deletes the 3rd contact shown in the current list.
@@ -79,41 +79,41 @@ Adds a student to the address book.
Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [g/GRADE] [s/SUBJECT] [d/DATETIME] [t/TAG]…`
:bulb: **Tip:**
-A person can have any number of tags (including 0)
+A student can have any number of tags (including 0)
-* Grade should only contain a single letter from A to D, with plus(+), minus(-) or neither.
+* Grade follows NUS grading system. (i.e. [A+, A, A-, B+, B, B-, C+, C, D+, D, F, -])
* DateTime should be in yyyy-mm-dd hhmm
Examples:
* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01 g/B+ s/Mathematics d/2024-02-03 1800`
-* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal`
+* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567`
-### Listing all persons : `list`
+### Listing all students : `list`
-Shows a list of all persons in the address book.
+Shows a list of all students in the address book.
Format: `list`
-### Editing a person : `edit`
+### Editing a student : `edit`
-Edits an existing person in the address book.
+Edits an existing student in the address book.
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, …
+* Edits the student at the specified `INDEX`. The index refers to the index number shown in the displayed student 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
+* When editing tags, the existing tags of the student will be removed i.e adding of tags is not cumulative.
+* You can remove all the student’s tags by typing `t/` without
specifying any tags after it.
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.
+* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st student to be `91234567` and `johndoe@example.com` respectively.
+* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd student to be `Betsy Crower` and clears all existing tags.
-### Locating persons by name: `find`
+### Locating students by name: `find`
-Finds persons whose names contain any of the given keywords.
+Finds students whose names contain any of the given keywords.
Format: `find KEYWORD [MORE_KEYWORDS]`
@@ -121,7 +121,7 @@ Format: `find KEYWORD [MORE_KEYWORDS]`
* 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).
+* Students matching at least one keyword will be returned (i.e. `OR` search).
e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`
Examples:
@@ -129,19 +129,35 @@ Examples:
* `find alex david` returns `Alex Yeoh`, `David Li`
![result for 'find alex david'](images/findAlexDavidResult.png)
-### Deleting a person : `delete`
+### Filter student by grade / subject: `filter`
-Deletes the specified person from the address book.
+Filters student who has the specified grade and/or subject.
+
+Format: `filter [g/GRADE] [s/SUBJECT]`
+
+* Search is case-sensitive.
+* The order of keywords does not matter.
+* At least one of the optional fields must be provided.
+
+### View Schedule: `view`
+
+Displays a calendar to view scheduled classes.
+
+Format: `view`
+
+### Deleting a student : `delete`
+
+Deletes the specified student 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.
+* Deletes the student at the specified `INDEX`.
+* The index refers to the index number shown in the displayed student list.
* The index **must be a positive integer** 1, 2, 3, …
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.
+* `list` followed by `delete 2` deletes the 2nd student in the address book.
+* `find Betsy` followed by `delete 1` deletes the 1st student in the results of the `find` command.
### Clearing all entries : `clear`
@@ -189,14 +205,14 @@ _Details coming soon ..._
## 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`
-**Mark payment**|`mark_payment INDEX` e.g. `mark_payment 2`
-**payment_list**|`payment_list`
+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`
+**Filter**| `filter [g/GRADE] [s/SUBJECT]`
+**View Schedule**|`view`
+**List** | `list`
+**Help** | `help`
diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml
index 196d0d2caac..53d33fcd39d 100644
--- a/docs/diagrams/BetterModelClassDiagram.puml
+++ b/docs/diagrams/BetterModelClassDiagram.puml
@@ -4,13 +4,13 @@ skinparam arrowThickness 1.1
skinparam arrowColor MODEL_COLOR
skinparam classBackgroundColor MODEL_COLOR
-AddressBook *-right-> "1" UniquePersonList
+AddressBook *-right-> "1" UniquePersonAndDateTimeList
AddressBook *-right-> "1" UniqueTagList
-UniqueTagList -[hidden]down- UniquePersonList
-UniqueTagList -[hidden]down- UniquePersonList
+UniqueTagList -[hidden]down- UniquePersonAndDateTimeList
+UniqueTagList -[hidden]down- UniquePersonAndDateTimeList
UniqueTagList -right-> "*" Tag
-UniquePersonList -right-> Person
+UniquePersonAndDateTimeList -right-> Person
Person -up-> "*" Tag
diff --git a/docs/diagrams/FilterActivityDiagram.puml b/docs/diagrams/FilterActivityDiagram.puml
new file mode 100644
index 00000000000..e5dd5461703
--- /dev/null
+++ b/docs/diagrams/FilterActivityDiagram.puml
@@ -0,0 +1,14 @@
+@startuml
+skin rose
+skinparam ActivityFontSize 15
+skinparam ArrowFontSize 12
+start
+:User executes filter command;
+
+'Since the beta syntax does not support placing the condition outside the
+'diamond we place it as the true branch instead.
+
+:System updates filter predicate;
+:Returns filtered student list;
+stop
+@enduml
diff --git a/docs/diagrams/FilterSequenceDiagram-Logic.puml b/docs/diagrams/FilterSequenceDiagram-Logic.puml
new file mode 100644
index 00000000000..7ebbe87b080
--- /dev/null
+++ b/docs/diagrams/FilterSequenceDiagram-Logic.puml
@@ -0,0 +1,55 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":FilterCommand" as FilterCommand LOGIC_COLOR
+participant ":GradeSubjectFilterPredicate" as GradeSubjectFilterPredicate LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+[-> LogicManager : execute(filter g/...)
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand()
+activate AddressBookParser
+
+create GradeSubjectFilterPredicate
+AddressBookParser -> GradeSubjectFilterPredicate
+activate GradeSubjectFilterPredicate
+
+GradeSubjectFilterPredicate --> AddressBookParser
+deactivate GradeSubjectFilterPredicate
+
+create FilterCommand
+AddressBookParser -> FilterCommand
+activate FilterCommand
+
+FilterCommand --> AddressBookParser
+deactivate FilterCommand
+
+AddressBookParser --> LogicManager
+deactivate AddressBookParser
+
+LogicManager -> FilterCommand : execute()
+activate FilterCommand
+
+FilterCommand -> Model : updateFilteredPersonList(predicate)
+activate Model
+
+Model --> FilterCommand
+deactivate Model
+
+FilterCommand --> LogicManager : commandResult
+deactivate FilterCommand
+FilterCommand -[hidden]-> LogicManager : commandResult
+destroy FilterCommand
+destroy GradeSubjectFilterPredicate
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/FilterSequenceDiagram-Model.puml b/docs/diagrams/FilterSequenceDiagram-Model.puml
new file mode 100644
index 00000000000..b7e69c2e09f
--- /dev/null
+++ b/docs/diagrams/FilterSequenceDiagram-Model.puml
@@ -0,0 +1,17 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> Model : updateFilteredPersonList(predicate)
+activate Model
+
+Model -> Model :setPredicate(predicate)
+
+[<-- Model
+deactivate Model
+
+@enduml
diff --git a/docs/diagrams/FilterState0.puml b/docs/diagrams/FilterState0.puml
new file mode 100644
index 00000000000..6efbd2e7b17
--- /dev/null
+++ b/docs/diagrams/FilterState0.puml
@@ -0,0 +1,18 @@
+@startuml
+!include style.puml
+skinparam ClassFontColor #000000
+skinparam ClassBorderColor #000000
+skinparam ClassBackgroundColor #FFFFAA
+
+title FilteredList
+
+package Predicates {
+ class State1 as "Model.PREDICATE_SHOW_ALL_PERSONS"
+ class State2 as ":GradeSubjectFilterPredicate"
+}
+State1 -[hidden]right-> State2
+hide State2
+
+class Pointer as "Current State" #FFFFFF
+Pointer -up-> State1
+@end
diff --git a/docs/diagrams/FilterState1.puml b/docs/diagrams/FilterState1.puml
new file mode 100644
index 00000000000..ed638134a5e
--- /dev/null
+++ b/docs/diagrams/FilterState1.puml
@@ -0,0 +1,17 @@
+@startuml
+!include style.puml
+skinparam ClassFontColor #000000
+skinparam ClassBorderColor #000000
+skinparam ClassBackgroundColor #FFFFAA
+
+title FilteredList
+
+package Predicates {
+ class State1 as "Model.PREDICATE_SHOW_ALL_PERSONS"
+ class State2 as ":GradeSubjectFilterPredicate"
+}
+State1 -[hidden]right-> State2
+
+class Pointer as "Current State" #FFFFFF
+Pointer -up-> State2
+@end
diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml
index 6b1ec31b1c0..47e6b3032fc 100644
--- a/docs/diagrams/ModelClassDiagram.puml
+++ b/docs/diagrams/ModelClassDiagram.puml
@@ -12,7 +12,7 @@ Class AddressBook
Class ModelManager
Class UserPrefs
-Class UniquePersonList
+Class UniquePersonAndDateTimeList
Class Person
Class Address
Class Email
@@ -40,8 +40,8 @@ ModelManager -left-> "1" AddressBook
ModelManager -right-> "1" UserPrefs
UserPrefs .up.|> ReadOnlyUserPrefs
-AddressBook *--> "1" UniquePersonList
-UniquePersonList --> "~* all" Person
+AddressBook *--> "1" UniquePersonAndDateTimeList
+UniquePersonAndDateTimeList --> "~* all" Person
Person *--> Name
Person *--> Phone
Person *--> Email
diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml
index a821e06458c..3878fd7fe0c 100644
--- a/docs/diagrams/StorageClassDiagram.puml
+++ b/docs/diagrams/StorageClassDiagram.puml
@@ -19,6 +19,7 @@ Class "<>\nAddressBookStorage" as AddressBookStorage
Class JsonAddressBookStorage
Class JsonSerializableAddressBook
Class JsonAdaptedPerson
+Class JsonAdaptedDateTime
Class JsonAdaptedTag
}
@@ -38,6 +39,7 @@ JsonUserPrefsStorage .up.|> UserPrefsStorage
JsonAddressBookStorage .up.|> AddressBookStorage
JsonAddressBookStorage ..> JsonSerializableAddressBook
JsonSerializableAddressBook --> "*" JsonAdaptedPerson
+JsonAdaptedPerson --> "*" JsonAdaptedDateTime
JsonAdaptedPerson --> "*" JsonAdaptedTag
@enduml
diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml
index 95473d5aa19..c1e4a97fde2 100644
--- a/docs/diagrams/UiClassDiagram.puml
+++ b/docs/diagrams/UiClassDiagram.puml
@@ -10,6 +10,7 @@ Class "{abstract}\nUiPart" as UiPart
Class UiManager
Class MainWindow
Class HelpWindow
+Class ViewWindow
Class ResultDisplay
Class PersonListPanel
Class PersonCard
@@ -35,6 +36,7 @@ MainWindow *-down-> "1" ResultDisplay
MainWindow *-down-> "1" PersonListPanel
MainWindow *-down-> "1" StatusBarFooter
MainWindow --> "0..1" HelpWindow
+MainWindow --> "0..1" ViewWindow
PersonListPanel -down-> "*" PersonCard
@@ -46,6 +48,7 @@ PersonListPanel --|> UiPart
PersonCard --|> UiPart
StatusBarFooter --|> UiPart
HelpWindow --|> UiPart
+ViewWindow --|> UiPart
PersonCard ..> Model
UiManager -right-> Logic
@@ -53,6 +56,7 @@ MainWindow -left-> Logic
PersonListPanel -[hidden]left- HelpWindow
HelpWindow -[hidden]left- CommandBox
+ViewWindow -[hidden]left- CommandBox
CommandBox -[hidden]left- ResultDisplay
ResultDisplay -[hidden]left- StatusBarFooter
diff --git a/docs/images/FilterActivityDiagram.png b/docs/images/FilterActivityDiagram.png
new file mode 100644
index 00000000000..db76f5c722b
Binary files /dev/null and b/docs/images/FilterActivityDiagram.png differ
diff --git a/docs/images/FilterSequenceDiagram-Logic.png b/docs/images/FilterSequenceDiagram-Logic.png
new file mode 100644
index 00000000000..41fa75e4121
Binary files /dev/null and b/docs/images/FilterSequenceDiagram-Logic.png differ
diff --git a/docs/images/FilterSequenceDiagram-Model.png b/docs/images/FilterSequenceDiagram-Model.png
new file mode 100644
index 00000000000..f96766f4408
Binary files /dev/null and b/docs/images/FilterSequenceDiagram-Model.png differ
diff --git a/docs/images/FilterState0-FilteredList.png b/docs/images/FilterState0-FilteredList.png
new file mode 100644
index 00000000000..8540f3acb4d
Binary files /dev/null and b/docs/images/FilterState0-FilteredList.png differ
diff --git a/docs/images/FilterState1-FilteredList.png b/docs/images/FilterState1-FilteredList.png
new file mode 100644
index 00000000000..e645897e941
Binary files /dev/null and b/docs/images/FilterState1-FilteredList.png differ
diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png
index 18fa4d0d51f..6ae5620b2e7 100644
Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ
diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png
index 11f06d68671..e4eeec1733a 100644
Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ
diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png
index b1f70470137..1942f2d7e38 100644
Binary files a/docs/images/helpMessage.png and b/docs/images/helpMessage.png differ
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java
index b2d2f1b8792..1bb0618bd0d 100644
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ b/src/main/java/seedu/address/logic/commands/AddCommand.java
@@ -50,6 +50,7 @@ public class AddCommand extends Command {
public static final String MESSAGE_SUCCESS = "New Student added: %1$s";
public static final String MESSAGE_DUPLICATE_PERSON = "This student already exists in the address book";
+ public static final String MESSAGE_DUPLICATE_DATETIME = "This datetime already exists in the address book";
private final Person toAdd;
@@ -69,6 +70,10 @@ public CommandResult execute(Model model) throws CommandException {
throw new CommandException(MESSAGE_DUPLICATE_PERSON);
}
+ if (model.hasDateTime(toAdd)) {
+ throw new CommandException(MESSAGE_DUPLICATE_DATETIME);
+ }
+
model.addPerson(toAdd);
return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(toAdd)));
}
diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java
index 249b6072d0d..0690cd79d66 100644
--- a/src/main/java/seedu/address/logic/commands/CommandResult.java
+++ b/src/main/java/seedu/address/logic/commands/CommandResult.java
@@ -19,13 +19,17 @@ public class CommandResult {
/** The application should exit. */
private final boolean exit;
+ /** The application show the schedule to the user. */
+ private final boolean showView;
+
/**
* Constructs a {@code CommandResult} with the specified fields.
*/
- public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
+ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit, boolean showView) {
this.feedbackToUser = requireNonNull(feedbackToUser);
this.showHelp = showHelp;
this.exit = exit;
+ this.showView = showView;
}
/**
@@ -33,7 +37,7 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
* and other fields set to their default value.
*/
public CommandResult(String feedbackToUser) {
- this(feedbackToUser, false, false);
+ this(feedbackToUser, false, false, false);
}
public String getFeedbackToUser() {
@@ -48,6 +52,10 @@ public boolean isExit() {
return exit;
}
+ public boolean isView() {
+ return showView;
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
@@ -62,12 +70,13 @@ public boolean equals(Object other) {
CommandResult otherCommandResult = (CommandResult) other;
return feedbackToUser.equals(otherCommandResult.feedbackToUser)
&& showHelp == otherCommandResult.showHelp
- && exit == otherCommandResult.exit;
+ && exit == otherCommandResult.exit
+ && showView == otherCommandResult.showView;
}
@Override
public int hashCode() {
- return Objects.hash(feedbackToUser, showHelp, exit);
+ return Objects.hash(feedbackToUser, showHelp, exit, showView);
}
@Override
@@ -76,6 +85,7 @@ public String toString() {
.add("feedbackToUser", feedbackToUser)
.add("showHelp", showHelp)
.add("exit", exit)
+ .add("showView", showView)
.toString();
}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
index 1135ac19b74..5fa51087c6c 100644
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
@@ -23,7 +23,7 @@ public class DeleteCommand extends Command {
+ "Parameters: INDEX (must be a positive integer)\n"
+ "Example: " + COMMAND_WORD + " 1";
- public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
+ public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Student: %1$s";
private final Index targetIndex;
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java
index 01296a56ece..93840a23e04 100644
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ b/src/main/java/seedu/address/logic/commands/EditCommand.java
@@ -66,6 +66,7 @@ public class EditCommand extends Command {
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.";
+ public static final String MESSAGE_DUPLICATE_DATETIME = "This datetime already exists in the address book";
private final Index index;
private final EditPersonDescriptor editPersonDescriptor;
@@ -98,6 +99,10 @@ public CommandResult execute(Model model) throws CommandException {
throw new CommandException(MESSAGE_DUPLICATE_PERSON);
}
+ if (!personToEdit.isSameDateTime(editedPerson) && model.hasDateTime(editedPerson)) {
+ throw new CommandException(MESSAGE_DUPLICATE_DATETIME);
+ }
+
model.setPerson(personToEdit, editedPerson);
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)));
diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java
index 3dd85a8ba90..acac9a21374 100644
--- a/src/main/java/seedu/address/logic/commands/ExitCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java
@@ -13,7 +13,7 @@ public class ExitCommand extends Command {
@Override
public CommandResult execute(Model model) {
- return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true);
+ return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/FilterCommand.java b/src/main/java/seedu/address/logic/commands/FilterCommand.java
index df3f69e64c4..b5935795a17 100644
--- a/src/main/java/seedu/address/logic/commands/FilterCommand.java
+++ b/src/main/java/seedu/address/logic/commands/FilterCommand.java
@@ -17,7 +17,7 @@ public class FilterCommand extends Command {
public static final String MESSAGE_USAGE = COMMAND_WORD
+ ": Filters the address book based on the Grade or Subject.\n"
- + "Parameters: \n"
+ + "Parameters: [g/GRADE] [s/SUBJECT]\n"
+ "Example: " + COMMAND_WORD + " g/A s/Maths";
public static final String MESSAGE_FILTER_ADDRESS_BOOK_SUCCESS = "Filtered address book by %2$s!\n";
@@ -34,7 +34,7 @@ public CommandResult execute(Model model) throws CommandException {
model.updateFilteredPersonList(predicate);
return new CommandResult(
String.format(MESSAGE_FILTER_ADDRESS_BOOK_SUCCESS + Messages.MESSAGE_PERSONS_LISTED_OVERVIEW,
- model.getFilteredPersonList().size(), predicate));
+ model.getFilteredPersonList().size(), predicate.filterResult()));
}
@Override
diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java
index bf824f91bd0..07d26e2a23c 100644
--- a/src/main/java/seedu/address/logic/commands/HelpCommand.java
+++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java
@@ -16,6 +16,6 @@ public class HelpCommand extends Command {
@Override
public CommandResult execute(Model model) {
- return new CommandResult(SHOWING_HELP_MESSAGE, true, false);
+ return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/ViewCommand.java b/src/main/java/seedu/address/logic/commands/ViewCommand.java
new file mode 100644
index 00000000000..dacd330aa83
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ViewCommand.java
@@ -0,0 +1,17 @@
+package seedu.address.logic.commands;
+
+import seedu.address.model.Model;
+
+/**
+ * Format full help instructions for every command for display.
+ */
+public class ViewCommand extends Command {
+
+ public static final String COMMAND_WORD = "view";
+ public static final String SHOWING_SCHEDULE_MESSAGE = "Opened schedule window.";
+
+ @Override
+ public CommandResult execute(Model model) {
+ return new CommandResult(SHOWING_SCHEDULE_MESSAGE, false, false, true);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
index 3d1b98f57dc..c0b5f0523f0 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
@@ -18,6 +18,7 @@
import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.ViewCommand;
import seedu.address.logic.parser.exceptions.ParseException;
/**
@@ -75,12 +76,15 @@ public Command parseCommand(String userInput) throws ParseException {
case FilterCommand.COMMAND_WORD:
return new FilterCommandParser().parse(arguments);
- case ExitCommand.COMMAND_WORD:
- return new ExitCommand();
-
case HelpCommand.COMMAND_WORD:
return new HelpCommand();
+ case ViewCommand.COMMAND_WORD:
+ return new ViewCommand();
+
+ case ExitCommand.COMMAND_WORD:
+ return new ExitCommand();
+
default:
logger.finer("This user input caused a ParseException: " + userInput);
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
index 73397161e84..7a428852d6e 100644
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ b/src/main/java/seedu/address/model/AddressBook.java
@@ -7,7 +7,7 @@
import javafx.collections.ObservableList;
import seedu.address.commons.util.ToStringBuilder;
import seedu.address.model.person.Person;
-import seedu.address.model.person.UniquePersonList;
+import seedu.address.model.person.UniquePersonAndDateTimeList;
/**
* Wraps all data at the address-book level
@@ -15,7 +15,7 @@
*/
public class AddressBook implements ReadOnlyAddressBook {
- private final UniquePersonList persons;
+ private final UniquePersonAndDateTimeList persons;
/*
* The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
@@ -25,7 +25,7 @@ public class AddressBook implements ReadOnlyAddressBook {
* among constructors.
*/
{
- persons = new UniquePersonList();
+ persons = new UniquePersonAndDateTimeList();
}
public AddressBook() {}
@@ -67,6 +67,14 @@ public boolean hasPerson(Person person) {
return persons.contains(person);
}
+ /**
+ * Returns true if a person with the same datetime as {@code person} exists in the address book.
+ */
+ public boolean hasDateTime(Person person) {
+ requireNonNull(person);
+ return persons.containsDateTime(person);
+ }
+
/**
* Adds a person to the address book.
* The person must not already exist in the address book.
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..3c69596ccf2 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -57,6 +57,11 @@ public interface Model {
*/
boolean hasPerson(Person person);
+ /**
+ * Returns true if a person with the same datetime as {@code person} exists in the address book.
+ */
+ boolean hasDateTime(Person person);
+
/**
* Deletes the given person.
* The person must exist in the address book.
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 57bc563fde6..ac8c2e741e9 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -93,6 +93,12 @@ public boolean hasPerson(Person person) {
return addressBook.hasPerson(person);
}
+ @Override
+ public boolean hasDateTime(Person person) {
+ requireNonNull(person);
+ return addressBook.hasDateTime(person);
+ }
+
@Override
public void deletePerson(Person target) {
addressBook.removePerson(target);
diff --git a/src/main/java/seedu/address/model/person/DateTime.java b/src/main/java/seedu/address/model/person/DateTime.java
index fef24b789ba..acdf3fc2360 100644
--- a/src/main/java/seedu/address/model/person/DateTime.java
+++ b/src/main/java/seedu/address/model/person/DateTime.java
@@ -12,7 +12,8 @@
* Guarantees: immutable; dateTime is valid as declared in {@link #isValidDateTime(String)}
*/
public class DateTime {
- public static final String MESSAGE_CONSTRAINTS = "DateTime should be in the format yyyy-mm-dd hhmm";
+ public static final String MESSAGE_CONSTRAINTS = "DateTime should be in the format yyyy-mm-dd hhmm and"
+ + " a valid date and time";
public static final String VALIDATION_REGEX = "\\d{4}-\\d{2}-\\d{2} \\d{4}";
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd HHmm")
.withResolverStyle(ResolverStyle.STRICT);
diff --git a/src/main/java/seedu/address/model/person/GradeSubjectFilterPredicate.java b/src/main/java/seedu/address/model/person/GradeSubjectFilterPredicate.java
index bd974a49ebb..7e50cadc3a5 100644
--- a/src/main/java/seedu/address/model/person/GradeSubjectFilterPredicate.java
+++ b/src/main/java/seedu/address/model/person/GradeSubjectFilterPredicate.java
@@ -36,6 +36,16 @@ public boolean test(Person person) {
return isGradeFiltered && isSubjectFiltered;
}
+ /**
+ * Returns a String representation of the filter result for GUI display.
+ *
+ * @return Filter result.
+ */
+ public String filterResult() {
+ return "Grade: " + filteredGrade.toString() + ","
+ + "Subject: " + filteredSubject.toString();
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java
index 9e29e584ab7..7a6fa0fb011 100644
--- a/src/main/java/seedu/address/model/person/Person.java
+++ b/src/main/java/seedu/address/model/person/Person.java
@@ -103,6 +103,18 @@ public boolean isSamePerson(Person otherPerson) {
&& otherPerson.getName().equals(getName());
}
+ /**
+ * Returns true if both persons have the same set of datetime.
+ */
+ public boolean isSameDateTime(Person otherPerson) {
+ if (otherPerson == this) {
+ return true;
+ }
+
+ return otherPerson != null
+ && otherPerson.getDateTimes().equals(getDateTimes());
+ }
+
/**
* Returns true if both persons have the same identity and data fields.
* This defines a stronger notion of equality between two persons.
diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonAndDateTimeList.java
similarity index 76%
rename from src/main/java/seedu/address/model/person/UniquePersonList.java
rename to src/main/java/seedu/address/model/person/UniquePersonAndDateTimeList.java
index cc0a68d79f9..c4b3852fbf9 100644
--- a/src/main/java/seedu/address/model/person/UniquePersonList.java
+++ b/src/main/java/seedu/address/model/person/UniquePersonAndDateTimeList.java
@@ -5,9 +5,11 @@
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
+import seedu.address.model.person.exceptions.DuplicateDateTimeException;
import seedu.address.model.person.exceptions.DuplicatePersonException;
import seedu.address.model.person.exceptions.PersonNotFoundException;
@@ -15,14 +17,14 @@
* 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.
+ * unique in terms of identity in the UniquePersonAndDateTimeList. 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 {
+public class UniquePersonAndDateTimeList implements Iterable {
private final ObservableList internalList = FXCollections.observableArrayList();
private final ObservableList internalUnmodifiableList =
@@ -36,15 +38,32 @@ public boolean contains(Person toCheck) {
return internalList.stream().anyMatch(toCheck::isSamePerson);
}
+ /**
+ * Returns true if the list contains an equivalent DateTime as the given argument.
+ */
+ public boolean containsDateTime(Person toCheck) {
+ requireNonNull(toCheck);
+ Set toCheckDateTime = toCheck.getDateTimes();
+
+ return internalList.stream()
+ .flatMap(person -> person.getDateTimes().stream())
+ .anyMatch(toCheckDateTime::contains);
+ }
+
/**
* Adds a person to the list.
* The person must not already exist in the list.
+ * The person must not already have an existing datetime in the list.
*/
public void add(Person toAdd) {
requireNonNull(toAdd);
if (contains(toAdd)) {
throw new DuplicatePersonException();
}
+
+ if (containsDateTime(toAdd)) {
+ throw new DuplicateDateTimeException();
+ }
internalList.add(toAdd);
}
@@ -79,7 +98,7 @@ public void remove(Person toRemove) {
}
}
- public void setPersons(UniquePersonList replacement) {
+ public void setPersons(UniquePersonAndDateTimeList replacement) {
requireNonNull(replacement);
internalList.setAll(replacement.internalList);
}
@@ -116,12 +135,12 @@ public boolean equals(Object other) {
}
// instanceof handles nulls
- if (!(other instanceof UniquePersonList)) {
+ if (!(other instanceof UniquePersonAndDateTimeList)) {
return false;
}
- UniquePersonList otherUniquePersonList = (UniquePersonList) other;
- return internalList.equals(otherUniquePersonList.internalList);
+ UniquePersonAndDateTimeList otherUniquePersonAndDateTimeList = (UniquePersonAndDateTimeList) other;
+ return internalList.equals(otherUniquePersonAndDateTimeList.internalList);
}
@Override
diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicateDateTimeException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicateDateTimeException.java
new file mode 100644
index 00000000000..a63433eeff0
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/exceptions/DuplicateDateTimeException.java
@@ -0,0 +1,11 @@
+package seedu.address.model.person.exceptions;
+
+/**
+ * Signals that the operation will result in duplicate DateTime.
+ */
+public class DuplicateDateTimeException extends RuntimeException {
+ public DuplicateDateTimeException() {
+ super("Operation would result in 2 student having the same tutoring slot");
+ }
+}
+
diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java
index 35992a394d2..89763c1d8ba 100644
--- a/src/main/java/seedu/address/model/util/SampleDataUtil.java
+++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java
@@ -35,23 +35,23 @@ public static Person[] getSamplePersons() {
new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"),
new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), EMPTY_GRADE, EMPTY_SUBJECT,
new Attendance("Present"), new Payment("Paid"),
- getDateTimeSet("2024-03-02 1800", "2024-03-04 2000"), getTagSet("colleagues", "friends")),
+ getDateTimeSet("2024-03-02 1900", "2024-03-04 2000"), 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"), EMPTY_GRADE, EMPTY_SUBJECT,
new Attendance("Present"), new Payment("Paid"),
- getDateTimeSet("2024-03-02 1800", "2024-03-21 1600"), getTagSet("neighbours")),
+ getDateTimeSet("2024-03-02 2100", "2024-03-21 2200"), 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"), EMPTY_GRADE, EMPTY_SUBJECT,
new Attendance("Present"), new Payment("Paid"),
- getDateTimeSet("2024-03-14 2100"), getTagSet("family")),
+ getDateTimeSet("2024-03-14 2300"), 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"), EMPTY_GRADE, EMPTY_SUBJECT,
new Attendance("Present"), new Payment("Paid"),
- getDateTimeSet("2024-03-18 2200"), getTagSet("classmates")),
+ getDateTimeSet("2024-03-18 0100"), 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"), EMPTY_GRADE, EMPTY_SUBJECT,
new Attendance("Present"), new Payment("Paid"),
- getDateTimeSet("2024-03-28 2300"), getTagSet("colleagues"))
+ getDateTimeSet("2024-03-28 0200"), getTagSet("colleagues"))
};
}
diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
index 5efd834091d..c8b479089a2 100644
--- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
+++ b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
@@ -21,6 +21,8 @@ class JsonSerializableAddressBook {
public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s).";
+ public static final String MESSAGE_DUPLICATE_DATETIME = "Persons list contains duplicate datetime(s).";
+
private final List persons = new ArrayList<>();
/**
@@ -52,6 +54,9 @@ public AddressBook toModelType() throws IllegalValueException {
if (addressBook.hasPerson(person)) {
throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON);
}
+ if (addressBook.hasDateTime(person)) {
+ throw new IllegalValueException(MESSAGE_DUPLICATE_DATETIME);
+ }
addressBook.addPerson(person);
}
return addressBook;
diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java
index 79e74ef37c0..bb06dd70856 100644
--- a/src/main/java/seedu/address/ui/MainWindow.java
+++ b/src/main/java/seedu/address/ui/MainWindow.java
@@ -8,6 +8,7 @@
import javafx.scene.control.TextInputControl;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent;
+import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import seedu.address.commons.core.GuiSettings;
@@ -34,7 +35,10 @@ public class MainWindow extends UiPart {
private PersonListPanel personListPanel;
private ResultDisplay resultDisplay;
private HelpWindow helpWindow;
+ private ViewWindow viewWindow;
+ @FXML
+ private FlowPane calendar;
@FXML
private StackPane commandBoxPlaceholder;
@@ -66,6 +70,7 @@ public MainWindow(Stage primaryStage, Logic logic) {
setAccelerators();
helpWindow = new HelpWindow();
+ viewWindow = new ViewWindow(logic);
}
public Stage getPrimaryStage() {
@@ -147,7 +152,20 @@ public void handleHelp() {
}
}
+ /**
+ * Opens the view schedule window or focuses on it if it's already opened.
+ */
+ @FXML
+ public void handleView() {
+ if (!viewWindow.isShowing()) {
+ viewWindow.show();
+ } else {
+ viewWindow.focus();
+ }
+ }
+
void show() {
+ primaryStage.setMaxWidth(400);
primaryStage.show();
}
@@ -159,6 +177,7 @@ private void handleExit() {
GuiSettings guiSettings = new GuiSettings(primaryStage.getWidth(), primaryStage.getHeight(),
(int) primaryStage.getX(), (int) primaryStage.getY());
logic.setGuiSettings(guiSettings);
+ viewWindow.hide();
helpWindow.hide();
primaryStage.hide();
}
@@ -182,6 +201,10 @@ private CommandResult executeCommand(String commandText) throws CommandException
handleHelp();
}
+ if (commandResult.isView()) {
+ handleView();
+ }
+
if (commandResult.isExit()) {
handleExit();
}
diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java
index 2df9ee24c17..2741f423e12 100644
--- a/src/main/java/seedu/address/ui/PersonCard.java
+++ b/src/main/java/seedu/address/ui/PersonCard.java
@@ -8,6 +8,7 @@
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
+import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import seedu.address.model.person.Person;
@@ -15,8 +16,15 @@
* An UI component that displays information of a {@code Person}.
*/
public class PersonCard extends UiPart {
-
private static final String FXML = "PersonListCard.fxml";
+ private static final String PHONE_DESCRIPTION = "Phone: ";
+ private static final String ADDRESS_DESCRIPTION = "Address: ";
+ private static final String EMAIL_DESCRIPTION = "Email: ";
+ private static final String SUBJECT_DESCRIPTION = "Subject: ";
+ private static final String GRADE_DESCRIPTION = "Grade: ";
+ private static final String ATTENDANCE_DESCRIPTION = "Last class attendance: ";
+ private static final String PAYMENT_DESCRIPTION = "Current monthly fees status: ";
+ private static final String CELL_SMALL_LABEL_CLASS = "cell_small_label";
/**
* Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX.
@@ -35,19 +43,23 @@ public class PersonCard extends UiPart {
@FXML
private Label id;
@FXML
- private Label phone;
+ private HBox phone;
+ @FXML
+ private HBox address;
@FXML
- private Label address;
+ private HBox email;
@FXML
- private Label email;
+ private HBox subject;
@FXML
- private FlowPane subjectWithGrade;
+ private HBox grade;
@FXML
- private Label attendance;
+ private HBox attendance;
@FXML
- private Label payment;
+ private HBox payment;
@FXML
- private FlowPane dateTimes;
+ private HBox dateTimes;
+ @FXML
+ private Label dateTimeDescription;
@FXML
private FlowPane tags;
@@ -59,22 +71,41 @@ public PersonCard(Person person, int displayedIndex) {
this.person = person;
id.setText(displayedIndex + ". ");
name.setText(person.getName().fullName);
- phone.setText(person.getPhone().value);
- address.setText(person.getAddress().value);
- email.setText(person.getEmail().value);
- subjectWithGrade.getChildren().add(new Label(person.getSubject().value));
- subjectWithGrade.getChildren().add(new Label(person.getGrade().value));
- attendance.setText(person.getAttendance().value);
- payment.setText(person.getPayment().value);
- dateTimes.setHgap(5);
- person.getDateTimes().stream()
- .sorted(Comparator.comparing(dateTime -> dateTime.value))
- .forEach(dateTime -> dateTimes.getChildren()
- .add(new Label(LocalDateTime.parse(dateTime.value,
- DateTimeFormatter.ofPattern("uuuu-MM-dd HHmm"))
- .format(DateTimeFormatter.ofPattern("MMM d uuuu h:mma")))));
person.getTags().stream()
.sorted(Comparator.comparing(tag -> tag.tagName))
.forEach(tag -> tags.getChildren().add(new Label(tag.tagName)));
+ setField(phone, PHONE_DESCRIPTION, person.getPhone().value);
+ setField(address, ADDRESS_DESCRIPTION, person.getAddress().value);
+ setField(email, EMAIL_DESCRIPTION, person.getEmail().value);
+ setField(attendance, ATTENDANCE_DESCRIPTION, person.getAttendance().value);
+ setField(payment, PAYMENT_DESCRIPTION, person.getPayment().value);
+ setField(subject, SUBJECT_DESCRIPTION, person.getSubject().value);
+ setField(grade, GRADE_DESCRIPTION, person.getGrade().value);
+ person.getDateTimes().stream()
+ .sorted(Comparator.comparing(dateTime -> dateTime.value))
+ .forEach(dateTime -> {
+ Label dateTimeLabel = new Label(LocalDateTime.parse(dateTime.value,
+ DateTimeFormatter.ofPattern("uuuu-MM-dd HHmm"))
+ .format(DateTimeFormatter.ofPattern("MMM d uuuu h:mma")));
+ dateTimeLabel.getStyleClass().add(CELL_SMALL_LABEL_CLASS);
+ dateTimes.getChildren()
+ .add(dateTimeLabel);
+ });
+
+ }
+
+ public void setField(HBox hbox, String description, String value) {
+ Label descriptionLabel = new Label(description);
+ descriptionLabel.getStyleClass().add(CELL_SMALL_LABEL_CLASS);
+ HBox.setHgrow(descriptionLabel, Priority.NEVER);
+
+ Region spacer = new Region();
+ HBox.setHgrow(spacer, Priority.ALWAYS);
+
+
+ Label valueLabel = new Label(value);
+ valueLabel.getStyleClass().add(CELL_SMALL_LABEL_CLASS);
+ HBox.setHgrow(valueLabel, Priority.NEVER);
+ hbox.getChildren().addAll(descriptionLabel, spacer, valueLabel);
}
}
diff --git a/src/main/java/seedu/address/ui/ViewWindow.java b/src/main/java/seedu/address/ui/ViewWindow.java
new file mode 100644
index 00000000000..711580496bd
--- /dev/null
+++ b/src/main/java/seedu/address/ui/ViewWindow.java
@@ -0,0 +1,256 @@
+package seedu.address.ui;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.logging.Logger;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.geometry.Pos;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.layout.FlowPane;
+import javafx.scene.layout.StackPane;
+import javafx.scene.layout.VBox;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.text.Font;
+import javafx.scene.text.FontWeight;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.logic.Logic;
+import seedu.address.model.person.DateTime;
+import seedu.address.model.person.Person;
+
+/**
+ * Controller for a view schedule page
+ */
+public class ViewWindow extends UiPart {
+ private static final Logger logger = LogsCenter.getLogger(ViewWindow.class);
+ private static final String FXML = "Calendar.fxml";
+ private final Logic logic;
+ private LocalDateTime dateFocus;
+ private LocalDateTime today;
+
+ @FXML
+ private Text year;
+
+ @FXML
+ private Text month;
+
+ @FXML
+ private FlowPane calendar;
+
+ /**
+ * Creates a {@code viewWindow} with the given {@code Stage} and {@code Logic}.
+ */
+ public ViewWindow(Stage root, Logic logic) {
+ super(FXML, root);
+ this.logic = logic;
+ dateFocus = LocalDateTime.now();
+ today = LocalDateTime.now();
+ drawCalendar();
+ }
+
+ public ViewWindow(Logic logic) {
+ this(new Stage(), logic);
+ }
+
+ /**
+ * Shows the view schedule window.
+ * @throws IllegalStateException
+ *
+ *
+ * if this method is called on a thread other than the JavaFX Application Thread.
+ *
+ *
+ * if this method is called during animation or layout processing.
+ *
+ *
+ * if this method is called on the primary stage.
+ *
+ *
+ * if {@code dialogStage} is already showing.
+ *