diff --git a/README.md b/README.md index 16208adb9b6..cacd2ff07f7 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,24 @@ -[![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/AY2425S2-CS2103T-F13-3/tp/graph/badge.svg?token=D6PKJ60OR7)](https://codecov.io/gh/AY2425S2-CS2103T-F13-3/tp) ![Ui](docs/images/Ui.png) -* This is **a sample project for Software Engineering (SE) students**.
- Example usages: - * as a starting point of a course project (as opposed to writing everything from scratch) - * as a case study -* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details. - * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big. - * It comes with a **reasonable level of user and developer documentation**. -* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...). -* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**. -* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org/#contributing-to-se-edu) for more info. +## Overview +HireHive is a desktop application designed for small companies to efficiently track job candidates, manage interview notes, and streamline the hiring process. + +**Target users:** HR and recruitment managers for companies. + +### **Key Features** +- **Track Interview Stages & Notes** – Keep detailed records of interviews and past communications. +- **Bookmark Potential Candidates** – Mark standout applicants for future consideration. +- **Categorize Candidates** – Filter candidates by job role and recruitment stage. + +### Running our application: +1. Download our latest release (.jar or executable file). +2. In your terminal, run ```java -jar HireHive.jar``` +3. Enjoy :) + +For the detailed documentation of this project, see the [HireHive Product Website](https://github.com/AY2425S2-CS2103T-F13-3/tp) +This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). +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/#contributing-to-se-edu) for more info. + + diff --git a/build.gradle b/build.gradle index 0db3743584e..1b1c104befb 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { id 'jacoco' } -mainClassName = 'seedu.address.Main' +mainClassName = 'hirehive.address.Main' sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 @@ -20,6 +20,10 @@ checkstyle { toolVersion = '10.2' } +run { + enableAssertions = true +} + test { useJUnitPlatform() finalizedBy jacocoTestReport @@ -66,7 +70,7 @@ dependencies { } shadowJar { - archiveFileName = 'addressbook.jar' + archiveFileName = 'HireHive.jar' } defaultTasks 'clean', 'test' diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index eb761a9b9a7..35888555cbf 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -30,8 +30,8 @@ - - + + @@ -75,7 +75,7 @@ An import statement is unused if: It's not referenced in the file. --> - + diff --git a/docs/AboutUs.md b/docs/AboutUs.md index ff3f04abd02..f232e8283e8 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -9,51 +9,50 @@ You can reach us at the email `seer[at]comp.nus.edu.sg` ## Project team -### John Doe +### Wong Zenwei - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/zeotheburrito)] +[[portfolio](team/zeotheburrito.md)] * Role: Project Advisor -### Jane Doe +### Lam Yun Hong - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/m0destly)] +[[portfolio](team/m0destly.md)] -* Role: Team Lead -* Responsibilities: UI +* Role: Team Member +* Responsibilities: Help the team -### Johnny Doe +### Natalie Ong - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](http://github.com/nat-ong555)] [[portfolio](team/nat-ong555.md)] * Role: Developer -* Responsibilities: Data +* Responsibilities: Data, Documentation -### Jean Doe +### Deanna Poh - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/deannapoh)] +[[portfolio](team/deannapoh.md)] * Role: Developer -* Responsibilities: Dev Ops + Threading +* Responsibilities: UI/UX -### James Doe +### Caleb Cherng - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/ditzchann)] +[[portfolio](team/ditzchann.md)] * Role: Developer -* Responsibilities: UI +* Responsibilities: Testing, Code Quality diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 743c65a49d2..e1548a86eb0 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -9,7 +9,7 @@ title: Developer Guide ## **Acknowledgements** -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). -------------------------------------------------------------------------------------------------------------------- @@ -21,6 +21,12 @@ Refer to the guide [_Setting up and getting started_](SettingUp.md). ## **Design** +
+ +**:information_source: Note:**
+* Note that the class names with the name `AddressBook` are intentionally left as it is as our classes are named as such +
+
:bulb: **Tip:** The `.puml` files used to create diagrams in this document `docs/diagrams` folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams. @@ -36,7 +42,7 @@ Given below is a quick overview of main components and how they interact with ea **Main components of the architecture** -**`Main`** (consisting of classes [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java)) is in charge of the app launch and shut down. +**`Main`** (consisting of classes [`Main`](https://github.com/AY2425S2-CS2103T-F13-3/tp/blob/master/src/main/java/hirehive/address/Main.java) and [`MainApp`](https://github.com/AY2425S2-CS2103T-F13-3/tp/blob/master/src/main/java/hirehive/address/MainApp.java)) is in charge of the app launch and shut down. * At app launch, it initializes the other components in the correct sequence, and connects them up with each other. * At shut down, it shuts down the other components and invokes cleanup methods where necessary. @@ -51,9 +57,9 @@ The bulk of the app's work is done by the following four components: **How the architecture components interact with each other** -The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`. +The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete n/Alice`. - + Each of the four main components (also shown in the diagram above), @@ -68,13 +74,13 @@ The sections below give more details of each component. ### UI component -The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java) +The **API** of this component is specified in [`Ui.java`](https://github.com/AY2425S2-CS2103T-F13-3/tp/blob/master/src/main/java/hirehive/address/ui/Ui.java) ![Structure of the UI Component](images/UiClassDiagram.png) The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. -The `UI` 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/AY2425S2-CS2103T-F13-3/tp/blob/master/src/main/java/hirehive/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/AY2425S2-CS2103T-F13-3/tp/blob/master/src/main/resources/view/MainWindow.fxml) The `UI` component, @@ -85,15 +91,15 @@ The `UI` component, ### 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/AY2425S2-CS2103T-F13-3/tp/blob/master/src/main/java/hirehive/address/logic/Logic.java) Here's a (partial) class diagram of the `Logic` component: -The sequence diagram below illustrates the interactions within the `Logic` component, taking `execute("delete 1")` API call as an example. +The sequence diagram below illustrates the interactions within the `Logic` component, taking `execute("delete n/Alice")` API call as an example. -![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) +![Interactions Inside the Logic Component for the `delete n/Alice` Command](images/DeleteSequenceDiagram.png)
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of diagram.
@@ -115,7 +121,7 @@ How the parsing works: * All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. ### Model component -**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) +**API** : [`Model.java`](https://github.com/AY2425S2-CS2103T-F13-3/tp/blob/master/src/main/java/hirehive/address/model/Model.java) @@ -136,7 +142,7 @@ The `Model` component, ### Storage component -**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java) +**API** : [`Storage.java`](https://github.com/AY2425S2-CS2103T-F13-3/tp/blob/master/src/main/java/hirehive/address/storage/Storage.java) @@ -192,7 +198,7 @@ Step 4. The user now decides that adding the person was a mistake, and decides t
: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. -
+
z The following sequence diagram shows how an undo operation goes through the `Logic` component: @@ -237,13 +243,6 @@ The following activity diagram summarizes what happens when a user executes a ne * 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}_ - -### \[Proposed\] Data archiving - -_{Explain here how the data archiving feature will be implemented}_ - - -------------------------------------------------------------------------------------------------------------------- ## **Documentation, logging, testing, configuration, dev-ops** @@ -260,7 +259,9 @@ _{Explain here how the data archiving feature will be implemented}_ ### Product scope -**Target user profile**: +**Target user**: HR and recruitment manager for a small company + +**User profile**: * has a need to manage a significant number of contacts * prefer desktop apps over other types @@ -268,65 +269,440 @@ _{Explain here how the data archiving feature will be implemented}_ * prefers typing to mouse interactions * is reasonably comfortable using CLI apps -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +**Value proposition**: help the HR manager to effortlessly manage job candidates, interview notes, recruitment status and past communications in one place at a glance, simplifying recruitment with an intuitive, organized, and scalable solution. ### User stories Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` -| Priority | As a …​ | I want to …​ | So that I can…​ | -| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- | -| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App | -| `* * *` | user | add a new person | | -| `* * *` | user | delete a person | remove entries that I no longer need | -| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list | -| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident | -| `*` | user with many persons in the address book | sort persons by name | locate a person easily | - -*{More to be added}* +| Priority | As a …​ | I want to …​ | So that I can…​ | +|--------|------------|------------------------------|---------------------------------------------------------------------| +| `* * *` | HR manager | look at the information of the interviewees | so that I can review their qualifications for the role | +| `* * *` | HR manager | be able to add a new applicant’s information and contact details | I can keep track of potential candidates. | +| `* * *` | HR manager | delete the information of an applicant | I know which applicants do not qualify for an interview. | +| `* * *` | HR manager | be able to tag candidates with custom labels such as “Shortlisted” or “Rejected” | I can send out information more easily. | +| `* * *` | HR manager | be able to tag applicants based on their stages of application (into applicants/candidates/interviewees) | I know their progress in the interviews. | +| `* * *` | HR manager | save my candidate's information into my local storage | I can access it again in the future | +| `* *` | HR manager | be able to sort the interviewees based on the date of their interview | it is more convenient for me to keep track of who I am interviewing | +| `* *` | HR manager | be able to add interview notes to an interviewee profile | I can keep track of my opinions of them | +| `* *` | HR manager | be able to search for an interviewee’s name | I can quickly find their contact details | +| `* *` | HR manager | be able to see a list of all potential applicants | I can get an overview of how many candidates are applying | +| `* *` | HR manager | sort candidates by their custom labels/tags | I can group them together and send them the correct information. | +| `* *` | HR manager | receive reminders for upcoming interviews | I never miss a scheduled interview | +| `*` | HR manager | attach resumes and cover letters to each applicant’s profile | I can quickly review their qualifications. | +| `*` | HR manager | be able to automatically schedule interviews on applicants’ available timings | I am less likely to make mistakes during arrangement | +| `*` | HR manager | filter the applicants based on certain characteristics | I can efficiently decide who qualifies for an interview | +| `* *` | HR manager | be able to see the count of applicants by the respective stages of application | I can fill the job vacancies adequately. | ### 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 `HireHive` and the **Actor** is the `user`, unless specified otherwise) -**Use case: Delete a person** +**Use case: UC01: - Add a person** **MSS** -1. User requests to list persons -2. AddressBook shows a list of persons -3. User requests to delete a specific person in the list -4. AddressBook deletes the person +1. User chooses to add a person. +2. User enters the details of the person. +3. HireHive adds the person. +4. HireHive saves the details of the added person. +5. HireHive displays the added person. + + Use case ends. + +**Extensions** +* 2a. HireHive detects an invalid input in the entered details. + * 2a1. HireHive shows an error message. + * 2a2. User enters new data. + * Steps 2a1-2a2 are repeated until the user adds a valid input for the parameters. + * Use case resumes from step 3. + +* 2b. HireHive detects that the person already exists. + * 2b1. HireHive shows an error message. + * 2b2. User enters new data. + * Steps 2b1-2b2 are repeated until a person with a unique name is entered. + * Use case resumes from step 3. + +* 2c. HireHive detects an empty input for one of the parameters. + * 2c1. HireHive shows an error message. + * 2c2. User enters new data. + * Steps 2c1-2c2 are repeated until the user adds a valid input for all the parameters. + * Use case resumes from step 3. + +**Use case: UC02 - List all persons** + +**MSS** +1. User requests to list persons. +2. HireHive shows a list of persons. Use case ends. **Extensions** +* 1a. The list is empty. + * 1a1. HireHive displays an empty list. + + Use case ends. + +**Use case: UC03 - Delete a person** + +Preconditions: The list is not empty + +**MSS** + +1. User requests to list persons (UC02). +2. User requests to delete a specific person in the list, using either: + * The person's index in the list + * The person's name in the list +3. HireHive deletes the person -* 2a. The list is empty. + Use case ends. - Use case ends. +**Extensions** * 3a. The given index is invalid. - * 3a1. AddressBook shows an error message. + * 3a1. HireHive shows an error message. + + Use case resumes at step 1. + +* 3b. The given name does not match any person + * 3b1. HireHive shows an error message. + + Use case resumes at step 1. + +* 3c. Multiple persons match the given name + * 3c1. HireHive shows an error message, and requests for more specific data. + * 3c2. User enters new data + * 3c3. Steps 3c1-3c2 are repeated till the data entered is correct + + Use case resumes at step 3. + + +**Use case: UC04 - Tag a person** + +**MSS** + +1. User requests to list persons (UC02). +2. User specifies a person in HireHive to tag with a specific tag. +3. HireHive tags the person with the specific tag. +4. HireHive saves the details of the person tagged. +5. HireHive displays the updated details of the person tagged. + + Use case ends. + +**Extensions** + +* 3a. User specifies a person by a valid name, but HireHive has multiple people containing the same name. + + * 3a1. HireHive shows an error message. + * 3a2. HireHive shows a list of persons containing the name. + * 3a3. HireHive shows a message to ask user to enter the full name of the person to tag. + + Use case ends. + +* 3b. User specifies a person by a name of a person that does not exist. + * 3b1. HireHive shows an error message that no such person exists. + * 3b2. HireHive shows an empty list of persons. + + Use case ends. + +* 3c. User specifies a person by invalid index. + * 3c1. HireHive shows an error message that index is invalid. + + Use case ends. + +* 3d. User enters an invalid offset. + * 3d1. HireHive shows an error message that offset is invalid. + + Use case ends. + + + + +**Use case: UC05 - Edit a person** + +Preconditions: The list is not empty + +**MSS** + +1. User requests to edit a person. +2. User enters the index and parameter of the person to be edited. +3. HireHive edits the person at that index by replacing the old data at the specified parameter with what was input by the user. +4. HireHive saves the details of the edited person. +5. HireHive displays the person with their new details. + + Use case ends. + +**Extensions** + +* 2a. HireHive detects an invalid index. + * 2a1. HireHive shows an error message. + * 2a2. User enters new data. + * Steps 2a1-2a2 are repeated until the user inputs a valid index. + * Use case resumes from step 3. + +* 2b. HireHive detects an invalid parameter. + * 2b1. HireHive shows an error message. + * 2b2. User enters new data. + * Steps 2b1-2b2 are repeated until the user inputs a valid parameter. + * Use case resumes from step 3. + +* 2c. HireHive detects an empty input. + * 2c1. HireHive shows an error message. + * 2c2. User enters new data. + * Steps 2c1-2c2 are repeated until the user inputs valid inputs for both index and parameter. + * Use case resumes from step 3. + +* 2d. User tries to edit the name of a person at a valid index. + * 2d1. HireHive detects an existing person with the same name. + * 2d2. HireHive shows an error message. + * 2d3. User enters new data + * Steps 2d1-2d3 are repeated until the user inputs a unique name. + * Use case resumes from step 2. + +**Use case: UC06 - Filter the list** + +Preconditions: The list is not empty + +**MSS** + +1. User requests to filter persons based on a certain condition (name, tag, interview date). +2. User enters the search criteria. +3. HireHive displays the persons who fit the criteria. + + Use case ends. + +**Extensions** +* 2a. HireHive detects a empty or invalid search condition. + * 2a1. HireHive shows an error message. + * 2a2. User enters new data. + * Steps 2a1-2a2 are repeated until the user inputs a valid condition. + * Use case resumes from step 3. + +* 2b. No entry fits the criteria. + * 2b1. HireHive displays an empty list. + * Use case ends. + +Use case: UC07 - Schedule an interview + +**MSS** + +1. User requests to list persons (UC02). +2. User requests to schedule a meeting on a certain date. +3. HireHive adds the date to the person's entry. +4. HireHive saves the details of the person with the newly added date. +5. HireHive displays the details of the person with the newly added date. + + Use case ends. + +**Extensions** + +* 2a. User does not provide a date. + * 2a1. HireHive automatically schedules the interview date on the next free day. + * Use case resumes from step 3. +* 3a. The given index or name is invalid. + * 3a1. HireHive shows an error message. + +Use case resumes at step 1. + +**Use case: UC07 - Sorting persons** + +Preconditions: The list is not empty + +**MSS** + +1. User requests to sort the list. +2. HireHive sorts the list +3. HireHive displays sorted list. + + Use case ends. + +**Use case: UC08 - Add notes to a person** + +Preconditions: The list is not empty + +**MSS** + +1. User requests to add notes to a person. +2. User enters the name of the person and the contents of the note to be added. +3. HireHive adds the contents of the note to the person. +4. HireHive saves the note of the person. +5. HireHive displays the contents of the note in a popup window. - Use case resumes at step 2. + Use case ends. + +**Extensions** + +* 2a. HireHive detects that name is missing. + * 2a1. HireHive shows an error message. + * 2a2. User enters new input. + * Steps 2a1-2a2 are repeated until the user inputs an existing name. + * Use case resumes from step 3. +* 2b. HireHive detects that name is invalid. + * 2b2. HireHive shows an error message. + * 2a2. User enters new input. + * Steps 2b1-2b2 are repeated until the user inputs an existing name. + * Use case resumes from step 3. +* 2c. The name input by the user does not match any existing name. + * 2c1. HireHive shows an error message. + * 2c2. User enters new input. + * Steps 2c1-2c2 are repeated until the user inputs an existing name. + * Use case resumes from step 3. +* 2d. The name input by the user matches multiple names. + * 2d1. HireHive shows an error message. + * 2d2. User enters new input. + * Steps 2d1-2d2 are repeated until the user inputs the full name of the person. + * Use case resumes from step 3. +* 2e. HireHive detects that the note is invalid. + * 2e1. HireHive shows an error message. + * 2e2. User enters new input. + * Steps 2e1-2e2 are repeated until the user inputs a valid note. + * Use case resumes from step 3. +* 2f. The note input is empty. + * 2f1. HireHive accepts this as valid input and clears the note. + * Use case resumes from step 3. + +**Use case: UC09 - Display notes of a person** + +Preconditions: The list is not empty + +**MSS** + +1. User requests to display notes of a person. +2. User enters the name of the person. +3. HireHive displays the contents of the note in a popup window. + +**Use case: UC08 - Filtering out applicants by tag** + +Preconditions: The list is not empty + +**MSS** + +1. User requests to filter out the list with a tag +2. User enters the specific tag to filter out the list +3. HireHive displays all persons without the specified tag + + Use case ends. + +**Extensions** +* 2a. HireHive detects that name is missing. + * 2a1. HireHive shows an error message. + * 2a2. User enters new input. + * Steps 2a1-2a2 are repeated until the user inputs an existing name. + * Use case resumes from step 3. +* 2b. HireHive detects that name is invalid. + * 2b2. HireHive shows an error message. + * 2a2. User enters new input. + * Steps 2b1-2b2 are repeated until the user inputs an existing name. + * Use case resumes from step 3. +* 2c. The name input by the user does not match any existing name. + * 2c1. HireHive shows an error message. + * 2c2. User enters new input. + * Steps 2c1-2c2 are repeated until the user inputs an existing name. + * Use case resumes from step 3. +* 2d. The name input by the user matches multiple names. + * 2d1. HireHive shows an error message. + * 2d2. User enters new input. + * Steps 2d1-2d2 are repeated until the user inputs the full name of the person. + * Use case resumes from step 3. + +**Use case UC10 - Clear AddressBook** + +**MSS** + +1. User requests to clear the AddressBook. +2. User enters `clear` in the command box. +3. HireHive clears all existing data, and AddressBook is now empty. + + Use case ends. + +**Use case UC11 - Getting Help** + +Preconditions: User device has a working Internet connection. + +**MSS** + +1. User requests help in using HireHive. +2. User enters `help` in the command box. +3. HireHive opens the help window with a URL to the User Guide. +4. User copies the URL with the `Copy URL` button on the right side of the help window. +5. User accesses the User Guide through a browser after pasting the URL in. + + Use case ends. + +**Extensions** + +* 2a. User clicks on the `Help` button on the top left of the display window. + * 2a1. A dropdown display shows another `Help` button. + * 2a2. User clicks on the `Help` button. + * Use case resumes from step 3. +* 2b. User uses the shortcut to access the `Help` window. On both Mac and Windows, press `fn` + `F1`. + * Use case resumes from step 3. + +**Use case: UC12 - Filtering out applicants by tag** + +Preconditions: The list is not empty + +**MSS** + +1. User requests to filter out the list with a tag +2. User enters the specific tag to filter out the list +3. HireHive displays all persons without the specified tag + +**Extensions** +* 2a. HireHive detects an empty tag + * 2a1. HireHive shows an error message + * 2a2. User enters new data + * Steps 2a1-2a2 are repeated till user inputs a tag + + Use case resumes at step 3. + +* 2b. HireHive detects an unknown tag + * 2b1. HireHive shows an error message + * 2b2. User enters new data + * Steps 2b1-2b2 are repeated till users input a valid tag + + Use case resumes at step 3. + +**Use case UC13 - Exiting HireHive** + +**MSS** + +1. User requests to exit HireHive. +2. User enters `exit` as input. +3. HireHive saves the data in its current state and closes. + + Use case ends. + +**Extensions** + +* 2a. User clicks on the `File` button on the top left of the display window. + * 2a1. A dropdown display shows the `Exit` button. + * 2a2. User clicks on the `Exit` button. + * Use case resumes from step 3. +* 2b. User clicks on the close button of the display window. On Mac, it is in the top left corner. On Windows, it is in the top right corner. + * Use case resumes from step 3. -*{More to be added}* ### Non-Functional Requirements 1. Should work on any _mainstream OS_ as long as it has Java `17` 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. - -*{More to be added}* +4. Operations by the system should return responses in under 2 seconds. +5. Data should be backed up or stored in alternative formats, in the event that the default format which the data is loaded from becomes corrupted. +6. Data must be saved immediately upon change to ensure data persistence. ### Glossary * **Mainstream OS**: Windows, Linux, Unix, MacOS * **Private contact detail**: A contact detail that is not meant to be shared with others +* **Applicants**: Anyone who submits an application to the company +* **Candidates**: Applicants who are potentially qualified for the job +* **Interviewees**: Candidates that accept an interview offer +* **Offered**: Interviewees that receive job offers +* **Rejected**: Interviewees that did not receive job offers -------------------------------------------------------------------------------------------------------------------- @@ -354,29 +730,322 @@ testers are expected to do more *exploratory* testing. 1. Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained. -1. _{ more test cases …​ }_ +### Viewing help + +1. Viewing help page + + 1. Prerequisites: Application must be running. + + 2. Test case: `help`
+ Expected: A 'Help' window pops up, with the link to the user guide, that can be copied. + + 3. Test case: `help 1`
+ Expected: A 'Help' window pops up, with the link to the user guide, that can be copied. + +### Adding a person + +1. Adding a person + + 1. Prerequisites: Application must be running. + + 2. Test case: `add n/Betsy Crowe e/betsycrowe@example.com a/Ang Mo Kio Street 22 p/87654321 r/senior consulting analyst`
+ Expected: A person with name "Betsy Crowe", email "betsycrowe@example.com", address "Ang Mo Kio Street 22", phone number "87654321", applying for the "senior consulting analyst" role is added to the bottom of the existing list. Added person is given an index that follows immediately after the last assigned index. + + 3. Test case: `add n/John Doe a/Bishan Street 20 p/98765432 r/senior consulting intern`
+ Expected: No person is added. Error details shown in the status message. + + 4. Test case: `add n/John Doe e/john@gmail.com a/Bishan Street 20 p/98765432 r/senior consulting intern t/applicant`
+ Expected: No person is added. Error details shown in the status message. + + 5. Test case: `add n/John Doe e/john@gmail.com a/Bishan Street 20 p/98765432 r/senior consulting intern i/Likes to talk`
+ Expected: No person is added. Error details shown in the status message. + + 6. Test case: `add n/小丽 e/john@gmail.com a/Bishan Street 20 p/98765432 r/senior consulting intern`
+ Expected: No person is added. Error details shown in the status message. + +### Listing persons + +1. Listing all the persons in HireHive + + 1. Prerequisites: List is not empty + + 2. Test case: `list`
+ Expected: All the persons in the list are listed. Topmost person in the list is the oldest person who was added to the list, while the bottom-most person was the person who was most recently added to the list. Index of persons should start from 1 and increase from top to bottom of the list. + 3. Test case: `list help`
+ Expected: All the persons in the list are listed. Topmost person in the list is the oldest person who was added to the list, while the bottom-most person was the person who was most recently added to the list. Index of persons should start from 1 and increase from top to bottom of the list. + +### Sorting persons + +1. Sorting all the persons shown in the list. + + 1. Prerequisites: Multiple persons in the list, with some persons having interview dates + + 2. Test case: `sort`
+ Expected: All the persons in the list are sorted. Those with interview dates are sorted in chronological order, while those without interview dates are pushed to the back of the list, in the original order they were in previously. New index given based on the newly sorted order of the list. + + 3. Test case: `list help`
+ Expected: All the persons in the list are sorted. Those with interview dates are sorted in chronological order, while those without interview dates are pushed to the back of the list, in the original order they were in previously. New index given based on the newly sorted order of the list. ### Deleting a person -1. Deleting a person while all persons are being shown +1. Deleting a person by index 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. + + 2. Test case: `delete 1`
+ Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. - 1. Test case: `delete 1`
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. - - 1. Test case: `delete 0`
+ 3. Test case: `delete 0`
Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. - 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
+ 4. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
Expected: Similar to previous. -1. _{ more test cases …​ }_ +1. Deleting a person by name + 1. Prerequisites: List all persons using `list` command. Multiple persons in the list + + 2. Test case: `delete n/Alice`
+ Expected: Contact with the name "Alice" is deleted from the list. Details of the deleted contact shown in status message. + 3. Test case: `delete n/john doe`
+ Expected: Contact with the name "John Doe" is deleted from the list. Details of the deleted contact shown in status message. + 4. Test case: `delete n/nonexistent name`
+ Expected: Error message is shown, stating that no such contact exists in the list + 5. Test case: `delete john`
+ Expected: Error message for invalid command format is displayed in status message with example. + +### Tagging a person + +1. Tagging a person by name, with tag + 1. Prerequisites: Use the same persons list as when you first ran HireHive.jar + 2. Test case: `tag n/alice t/candidate`
+ Expected: Contact with name "Alice Yeoh" is tagged as "Candidate". Details of the tagged contact shown in the + status message. + 3. Test case: `tag n/alice t/CANDIDATE`
+ Expected: Contact with name "Alice Yeoh" is tagged as "Candidate". Details of the tagged contact shown in the + status message. + 4. Test case: `tag n/alice t/cAnDiDaTe`
+ Expected: Contact with name "Alice Yeoh" is tagged as "Candidate". Details of the tagged contact shown in the + status message. + 5. Test case: `tag n/ALICE t/candidate`
+ Expected: Contact with name "Alice Yeoh" is tagged as "Candidate". Details of the tagged contact shown in the + status message. + 6. Test case: `tag n/aLiCe t/CANDIDATE`
+ Expected: Contact with name "Alice Yeoh" is tagged as "Candidate". Details of the tagged contact shown in the + status message. + 7. Test case: `tag n/alice t/test`
+ Expected: Error message for invalid command format shown in status message with command format and example. +2. Tagging a person by index, with tag + 1. Prerequisites: Use the same persons list as when you first ran HireHive.jar + 2. Test case: `tag 1 t/interviewee`
+ Expected: First contact is tagged as "Interviewee". Details of the tagged contact shown in the + status message. + 3. Test case: `tag 1 t/INTERVIEWEE`
+ Expected: First contact is tagged as "Interviewee". Details of the tagged contact shown in the + status message. + 4. Test case: `tag 1 t/iNtErViEwEe`
+ Expected: First contact is tagged as "Interviewee". Details of the tagged contact shown in the + status message. + 5. Test case: `tag 1 t/test`
+ Expected: Error message for invalid command format shown in status message with command format and example. +3. Tagging a person by name, with offset + 1. Prerequisites: Use the same persons list as when you first ran HireHive.jar + 2. Test case: `tag +1 n/bernice`
+ Expected: Contact with name "Bernice Yu" is tagged as "Offered". Details of the tagged contact shown + in the status message (if tag was "Interviewee"). + 3. Test case `tag -2 n/charlotte`
+ Expected: Contact with name "Charlotte Oliveiro" is tagged as "Rejected". Details of the tagged contact shown + in the status message (if tag was "Candidate"). + +### Scheduling interview date + +1. Scheduling an interview date for a person by name + 1. Prerequisites: Use the same persons list as when you first ran HireHive.jar + 2. Test case: `schedule n/alice d/04/05/2025`
+ Expected: Contact with name "Alice Yeoh" has interview date scheduled as "04/05/2025". Details of the scheduled + contact shown in the status message. + 3. Test case: `schedule n/alice d/test`
+ Expected: Error message for invalid command format shown in status message with command format and example. + 4. Test case: `schedule n/alice 04/05/2025`
+ Expected: Error message for no persons found as there is no person with the name "alice 04/05/2025" +2. Scheduling an interview date for a person by index + 1. Prerequisites: Use the same persons list as when you first ran HireHive.jar + 2. Test case: `schedule 1 d/04/05/2025`
+ Expected: First contact has interview date scheduled as "04/05/2025". Details of the scheduled contact shown in the status message. + 3. Test case: `schedule 1 d/test`
+ Expected: Error message for invalid command format shown in status message with command format and example. + 4. Test case: `schedule 1 01/02/2025`
+ Expected: Error message for invalid integer as "1 01/02/2025" is not a non-zero unsigned integer. +3. Automatically scheduling the next interview date + 1. Prerequisites: Use the same persons list as when you first ran HireHive.jar + 2. Test case: `schedule 1`
+ Expected: First contact will be updated to tomorrow's date. + 3. Test case: `schedule n/alice`
+ Expected: Alice's contact will be updated to tomorrow's date + +### Show persons with upcoming interviews + +1. Show persons with upcoming interviews + 1. Prerequisites: Use the same persons list as when you first ran HireHive.jar. `list` command is run before the execution of the below commands. + 2. Test case: `remind 100`
+ Expected: Only the contact with name "David Li" and interview date "06/07/2025" is shown in the list. Success + message shown in the status message. + 3. Test case: `remind -1`
+ Expected: Error message for invalid command format shown in status message with command format and example. + 4. Test case: `remind 1000000000000000000000000000000`
+ Expected: Error message for invalid integer is shown. + +### Filtering for applicants with tag + +1. Filtering for applicants with specified tag + 1. Prerequisites: Use the same persons list as when you first ran HireHive.jar. `list` command is run before the execution of the below commands. + 2. Test case: `filter t/interviewee`
+ Expected: All contacts with the interviewee tag will be displayed on the list. + 3. Test case: `filter t/iNtErViEweE`
+ Expected: All contacts with the interviewee tag will be displayed on the list. + 4. Test case: `filter t/abc`
+ Expected: Error message showing the valid tags will be displayed. + 5. Test case `filter candidate`
+ Expected: Error message for invalid command format shown in status message with command format and example. + +### Filtering out applicants with tag + +1. Filtering out all applicants with specified tag, and the remaining list contains applicants without that tag + 1. Prerequisites: Use the same persons list as when you first ran HireHive.jar. `list` command is run before the execution of the below commands. + 2. Test case: `filterout t/interviewee`
+ Expected: All contacts without the interviewee tag will be displayed on the list + 3. Test case: `filterout t/Applicant`
+ Expected: All contacts without the applicant tag will be displayed on the list + 4. Test case: `filterout t/testing`
+ Expected: Error message showing the valid tags will be displayed. + 5. Test case `filterout candidate`
+ Expected: Error message for invalid command format shown in status message with command format and example. + +### Adding note of a person + +1. Adding note of a person + 1. Prerequisite: Use the initial persons list loaded when first running HireHive.jar + 2. Test case: `newnote n/Bernice Yu i/20 years old`
Expected output: "Added and displaying note of: Bernice Yu; Phone: 99272758; Email: berniceyu@example.com; Address: Blk 30 Lorong 3 Serangoon Gardens, #07-18; Role: HR manager; Date: 01/03/2025; Tag: INTERVIEWEE
Success: Applicant data has been saved." Additionally, popup window shows "20 years old" in white font. + 3. Test case: `newnote n/BeRnIcE i/19 years old`
Expected output: same as test case 1, but popup window shows "19 years old" in white font. + 4. Test case: `newnote n/ Bernice i/18 years old`
Expected output: same as test case 1, but popup window shows "18 years old" in white font. + 5. Test case: `newnote n/Yu i/`
Expected output: same as test case 1, but popup window shows "Currently empty..." in yellow font. + 6. Test case: `newnote n/bErNiCe yU i/Currently empty...`
Expected output: same as test case 1, but popup window shows "Currently empty..." in white font. + +### Displaying note of a person + +1. Displaying note of a person + 1. Prerequisite: Use the initial persons list loaded when first running HireHive.jar + 2. Test case: `displaynote n/Alice Yeoh`
Expected output: "Displaying note of: Alice Yeoh; Phone: 87438807; Email: alexyeoh@example.com; Address: Blk 30 Geylang Street 29, #06-40; Role: UI designer; Date: ; Tag: APPLICANT" Additionally, popup window shows "20 years old" in white font. + 3. Test case: `displaynote n/BeRnIcE yU`
Expected output: "Displaying note of: Bernice Yu; Phone: 99272758; Email: berniceyu@example.com; Address: Blk 30 Lorong 3 Serangoon Gardens, #07-18; Role: HR manager; Date: 01/03/2025; Tag: INTERVIEWEE" Additionally, popup window shows "Currently empty..." in yellow font. + 4. Test case: `displaynote n/Irfan`
Expected output: "Displaying note of: Irfan Ibrahim; Phone: 92492021; Email: irfan@example.com; Address: Blk 47 Tampines Street 20, #17-35; Role: Software Engineer; Date: 06/03/2025; Tag: REJECTED" Additionally, popup window shows "30 years old" in white font. + 5. Test case: `newnote n/raHIM i/`
Expected output: same as test case 3. + +### Editing the details of a person + +1. Editing the details of a person + 1. Prerequisites: Run `list` command and ensure there are at least 2 people in the list + 2. Test case: `edit 2 p/88888888`
+ Expected: The phone number of the 2nd person in the list is changed to 88888888 + 3. Test case: `edit 2 p/88888888 e/g@gmail.com`
+ Expected: The phone number and email of the 2nd person in the list is changed to 88888888 and g@gmail.com respectively. + 4. Test case: `edit 1 i/`
+ Expected: The note of the 1st person in the list is cleared. + 5. Test case: `edit 0 p/88888888` + Expected: Nobody's details are edited. Error is displayed. + +### Finding a person + +1. Finding a person with some keywords + 1. Prerequisites: Use the same persons list as when you first ran HireHive.jar. `list` command is run before the execution of the below commands. + 2. Test case: `find Alice` + Expected: Displays the applicant(s) whose name contains the keyword 'Alice' + +### Clearing all entries + +1. Clearing all entries in HireHive + + 1. Prerequisites: List contains people. + + 2. Test case: `clear`
+ Expected: All the persons in the list are cleared. List is now empty. + + 3. Test case: `Clear`
+ Expected: Nothing is cleared. Error details shown in the status message. + +### Exiting the program + +1. Exiting the program + + 1. Prerequisites: Program was originally running. + + 2. Test case: `exit`
+ Expected: Program exits and closes. + +### Loading data + +1. Sample data is loaded + 1. Test case: On first launch of `HireHive.jar`, this should automatically load the sample data.
Expected output: "Success: Sample applicant data has been loaded successfully." + 2. Test case: Delete `addressbook.json` under folder `data` if it exists, then launch `HireHive.jar`.
Expected output: same as test case i. + +2. Saved data is loaded + 1. Test case: perform a command that modifies data except `clear` e.g. `delete 1`, then `exit` the application. Relaunch `HireHive.jar`.
Expected output: "Success: Applicant data has been loaded successfully." + +3. Saved data is empty + 1. Test case: perform `clear` command e.g. `clear`, then `exit` the application. Relaunch `HireHive.jar`.
Expected output: "Current address book is empty. This might be due to corrupted data.
WARNING: Please check if data/addressbook.json has old corrupted data and attempt to fix it, otherwise any new successful commands will overwrite those contents." + +4. No data is loaded due to corruption of data + 1. Prerequisite: Launch `HireHive.jar` at least once and exit. + 2. Test case: Delete `addressbook.json` under folder `data` if it exists, then copy `config.json` into the same folder. Rename `config.json` to `addressbook.json`.
Expected output: "Current address book is empty. This might be due to corrupted data.
WARNING: Please check if data/addressbook.json has old corrupted data and attempt to fix it, otherwise any new successful commands will overwrite those contents." + +## **Appendix: Planned Enhancements** +**Team size - 5** + +### 1. Support multiple applicants with the same name + +**Current Feature Flaw:** + +HireHive currently does not allow duplicate names, which does not reflect real-world scenarios where different applicants may have identical names. + +**Proposed implementation:** + 1. Modify the `add` command to support adding multiple applicants with the same name. + * Compare applicants' phone numbers and emails instead of names to determine if they are the same applicant. + * In the real world, while 2 applicants may have the same name, no 2 applicants should have the same phone number or email. Thus, phone numbers and emails are a better way to reflect real-world scenarios and check for duplicates. + 2. Update `displaynote` and `newnote` command to also accept `INDEX`, instead of just a person's `NAME`. + * i.e. allow `displaynote INDEX` and `newnote INDEX i/NOTE`, on top of the current functionality, `displaynote n/NAME` and `newnote n/NAME i/NOTE` + * If applicants have the same name, they can be identified by their index instead, which is unique for each applicant. + * Index format: Each applicant is identified by their unique index + * Name format: + * Single match: Commands executes as per normal + * Multiple match (i.e. Multiple applicants with the same name) + * A list of applicants with the same name is displayed. + * Users can identify the desired applicant by using the command with the unique index of the person. + +### 2. Support phone numbers from different countries + +**Current Feature Flaw:** + +HireHive currently does not support international phone numbers. However, we understand that this may not be applicable in the real world as applicants may be on work permits that come to Singapore to work. + +**Proposed implementation:** + 1. Modify the `add` command to make it more flexible and allow international formats. + * Accept phone numbers with country codes. + * Allow for telephone numbers that start with numbers other than 9/8/6 and allow for longer telephone numbers. + +### 3. Prevent the app from refreshing when user types another command after sorting/filtering the list + +**Current Feature Flaw:** + +After a user sorts or filters the list, if the user enters another command like `delete`, HireHive currently resets the displayed list to its original state and the user loses the sorted or filtered list. This may be inconvenient for users as it would require them to filter or sort the list again. + +**Proposed implementation:** + 1. Modify `filter` and `sort` such that the displayed list is always retained unless the command `list` is entered. + +### 4. Make loading AddressBook message more specific -### Saving data +**Current Feature Flaw:** -1. Dealing with missing/corrupted data files +The current message for loading an empty AddressBook and loading an invalid AddressBook is the same. A warning is given to users who simply have an empty AddressBook, even though this behaviour is completely valid. - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ +**Proposed implementation:** +1. When an AddressBook with invalid data is loaded, show the error message "WARNING: Data in data/addressbook.json is corrupted. Please resolve it manually, otherwise any new successful commands will overwrite those contents." +2. When an empty AddressBook is loaded, show the message: "Success: No applicant data currently." -1. _{ more test cases …​ }_ diff --git a/docs/SettingUp.md b/docs/SettingUp.md index aef33ec72fd..d675296d601 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 `hirehive.address.Main` and try a few commands. 1. [Run the tests](Testing.md) to ensure they all pass. -------------------------------------------------------------------------------------------------------------------- diff --git a/docs/Testing.md b/docs/Testing.md index 8a99e82438a..f248056c811 100644 --- a/docs/Testing.md +++ b/docs/Testing.md @@ -31,6 +31,6 @@ This project has three types of tests: 1. *Unit tests* targeting the lowest level methods/classes.
e.g. `seedu.address.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` + e.g. `storage.hirehive.address.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. `logic.hirehive.address.LogicManagerTest` diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 27c2d1cf16c..678fa7b7d5f 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,42 +1,120 @@ --- 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} +# Welcome to HireHive! + +Tired of drowning in resumes and losing track of candidates? Meet HireHive— your **personal, all-in-one** hiring assistant designed to simplify the hiring process so you can focus on what matters most - finding the best talent. + +We combine the speed of keyboard commands with an intuitive visual interface to help you: +- Find top talent faster with **lightning-fast** keyboard shortcuts +- Stay effortlessly organized with all **[applicant](#glossary) details in one place** +- Make better hiring decisions with **structured interview notes** + +Ready to revolutionise your hiring? Let's begin! + +- [Quick start](#quick-start) +- [Command Summary](#command-summary) +- [Features](#features) + - [View help: `help`](#view-help-help) + - [Adding and modifying applicants](#adding-and-modifying-applicants) + - [Add an applicant: `add`](#add-an-applicant-add) + - [Edit an applicant: `edit`](#edit-an-applicant-edit) + - [Tag an applicant: `tag`](#tag-an-applicant-tag) + - [Add note to applicant: `newnote`](#add-note-to-applicant-newnote) + - [Schedule interview dates: `schedule`](#schedule-interview-dates-for-applicants--schedule) + - [Listing applicant information](#listing-applicant-information) + - [Display note of applicant: `displaynote`](#display-note-of-applicant-displaynote) + - [Sort applicants `sort`](#sort-applicants-sort) + - [List all applicants: `list`](#list-all-applicants-list) + - [Filtering applicant information](#filtering-applicant-information) + - [Filter applicants by tag: `filter`](#filter-applicants-by-tag-filter) + - [Filtering out applicants with tag: `filterout`](#filtering-out-applicants-with-tag-filterout) + - [Remind you of upcoming interviews: `remind`](#show-applicants-with-upcoming-interviews-remind) + - [Find applicants by name `find`](#find-applicants-by-name-find) + - [Deletion of applicants](#deletion-of-applicants) + - [Delete an applicant: `delete`](#delete-an-applicant-delete) + - [Clear all entries: `clear`](#clear-all-entries--clear) + - [Exit the program: `exit`](#exit-the-program--exit) + - [Save the data](#save-the-data) + - [Edit the data file](#edit-the-data-file) +- [FAQ](#faq) +- [Known Issues](#known-issues) +- [Glossary](#glossary) -------------------------------------------------------------------------------------------------------------------- -## Quick start -1. Ensure you have Java `17` or above installed in your Computer.
- **Mac users:** Ensure you have the precise JDK version prescribed [here](https://se-education.org/guides/tutorials/javaInstallationMac.html). +## Quick start +1. To use Hirehive, you need Java `17` or above installed in your computer.
+ - For **Mac users:** Ensure you have the precise version prescribed [here](https://se-education.org/guides/tutorials/javaInstallationMac.html). -1. Download the latest `.jar` file from [here](https://github.com/se-edu/addressbook-level3/releases). +2. Download the latest HireHive `.jar` file from [here](https://github.com/AY2425S2-CS2103T-F13-3/tp/releases). -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +3. Copy the downloaded file to a folder where you would like to store HireHive (e.g. "Documents" or "Desktop" or a new folder "HireHive") . -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) +4. Run HireHive. + - Find the folder that you stored HireHive previously (e.g. "Documents" or "Desktop" or "HireHive"). + - Right-click on the folder and choose: + - For **Mac users**: "New terminal at Folder". + - For **Windows users**: "Open in terminal". + - Type `java -jar hirehive.jar` command in the opened terminal and press Enter to run the HireHive application.
+ - A [GUI](#glossary) similar to the below should appear in a few seconds. Note how the app contains some sample data.
+ ![Ui](images/Ui-2.png) -1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
+5. To use HireHive, you can type a command in the command box and press Enter to execute it. e.g. typing `help` and pressing Enter will open the help window.
Some example commands you can try: - * `list` : Lists all contacts. + * `list` : Lists all applicants. + + * `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01 r/intern`: Adds an applicant named `John Doe` to HireHive, with his relevant information - * `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. + * `edit 3 n/Josef` : Edits the name of the 3rd applicant in the list to Josef - * `delete 3` : Deletes the 3rd contact shown in the current list. + * `find John Doe`: Searches for John Doe in the current list. - * `clear` : Deletes all contacts. + * `tag n/John Doe t/interviewee` : Tags John Doe with the 'interviewee' tag + + * `delete n/John Doe` : Deletes the applicant `John Doe` from the current list. * `exit` : Exits the app. -1. Refer to the [Features](#features) below for details of each command. +
+ + **:information_source: Note:**
+ Note that in HireHive, every applicant should **contain a [unique](#glossary) name**! +
+ +6. You can refer to the [Features](#features) section below for details of each command. + +[Back to top](#welcome-to-hirehive) + +-------------------------------------------------------------------------------------------------------------------- + +## Command summary + +| Action | Format, Examples | +|-------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **[Help](#view-help-help)** | `help` | +| **[Add](#add-an-applicant-add)** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS r/ROLE`
e.g. `add n/James Ho p/87654321 e/jamesho@example.com a/123, Clementi Rd, 1234665 r/software engineer intern` | +| **[Edit](#edit-an-applicant-edit)** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG] [i/NOTE] [id/DATE]`
e.g. `edit 2 n/James Lee e/jameslee@example.com` | +| **[Tag](#tag-an-applicant-tag)** | `tag n/NAME t/TAG`
e.g. `tag n/John Doe t/Applicant`
`tag INDEX t/TAG`
e.g. `tag 1 t/Candidate`
`tag OFFSET n/NAME`
e.g. `tag +1 n/John Doe` | +| **[NewNote](#add-notes-to-applicant-newnote)** | `newnote n/NAME i/NOTE`
e.g. `newnote n/John Doe i/25 years old` | +| **[Schedule](#schedule-interview-dates-for-applicants--schedule)** | 1. `schedule n/NAME [id/DATE]`
e.g. `schedule n/John Doe id/17/04/2025`
2. `schedule INDEX [id/DATE]`
e.g. `schedule 2 d/17/04/2025`
| +| **[DisplayNote](#display-note-of-applicant-displaynote)** | `displaynote n/NAME`
e.g. `displaynote n/John Doe` | +| **[Sort](#sort-applicants-sort)** | `sort` | +| **[List](#list-all-applicants-list)** | `list` | +| **[Filter](#filter-applicants-by-tag-filter)** | `filter t/TAG`
e.g. `filter t/Applicant` | +| **[FilterOut](#filtering-out-applicants-with-tag-filterout)** | `filterout t/TAG`
e.g. `filterout t/Applicant` | +| **[Reminder](#show-applicants-with-upcoming-interviews-remind)** | `remind DAYS`
e.g. `remind 3` | +| **[Find](#find-applicants-by-name-find)** | `find KEYWORD [MORE_KEYWORDS]`
e.g. `find James Jake` | +| **[Delete](#delete-an-applicant-delete)** | 1. `delete n/NAME`
e.g. `delete n/John Doe`
2. `delete INDEX`
e.g. `delete 2` | +| **[Clear](#clear-all-entries--clear)** | `clear` | +| **[Exit](#exit-the-program--exit)** | `exit` | + +[Back to top](#welcome-to-hirehive) -------------------------------------------------------------------------------------------------------------------- @@ -46,155 +124,475 @@ AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized fo **:information_source: Notes about the command format:**
-* Words in `UPPER_CASE` are the parameters to be supplied by the user.
+* Words in `UPPER_CASE` are the parameters to be supplied by you.
e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`. * Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. + e.g `n/NAME [i/NOTE]` can be used as `n/John Doe i/27 years old` 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. + e.g. `[t/TAG]…​` can be used as ` ` (i.e. 0 times), `t/interviewee`, `t/shortlisted` 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. -* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
+* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit`, `sort` and `clear`) + will be ignored.
e.g. if the command specifies `help 123`, it will be interpreted as `help`. - -* If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application. -### Viewing help : `help` +[Back to top](#welcome-to-hirehive) -Shows a message explaning how to access the help page. +### View help: `help` + +Shows a message explaining how you can access the help page that can assist you in understanding the different features of HireHive and how they work. ![help message](images/helpMessage.png) Format: `help` +[Back to top](#welcome-to-hirehive) + +### Adding and modifying applicants + +### Add an applicant: `add` + +You can add a new applicant to HireHive when someone new applies to your company! + +Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS r/ROLE` + +* NAME: + - Names should only contain **English letters**, **spaces**, and the following symbols: **, ( ) / . @ - '** + - Example: `Ali s/o Mohammed`, `Rachel-Natalie`, `Si Min, Rachel O' Connor` are allowed names. + - Names **should not be blank or start with a symbol**. + - Example: `@Natalie` is not allowed + - Please **do not use numbers** in names. + - Example: `R4chel` is not allowed + - Names are **[case-insensitive](#glossary)**, and is displayed as how you type it. + +* PHONE_NUMBER: + - Please input a phone number that **start with 9/8/6** and is **exactly 8 digits** long. + - Please **do not use spaces** + +* EMAIL: + - Emails should be of the format **local-part@domain** and adhere to the following constraints: + 1. local-part: + - The local-part should only contain **[alphanumeric](#glossary) characters** and the special characters: **+_.-** + - The local-part **should not start or end with any special characters**. + 2. domain: + - The domain name is made up of domain labels that may or may not be separated by periods. + - The domain name must: + - end with a domain label **at least 2 characters** long, and + - have each domain label **start and end with [alphanumeric](#glossary) characters**, and + - have each domain label consist of [alphanumeric](#glossary) characters, separated only by hyphens, if any. + +* ADDRESS: + - Addresses can take any values, and it should not be blank + +* ROLE: + - Roles should only **contain [alphanumeric](#glossary) characters and spaces**, and it should not be blank. + + +
+ +**:information_source: Note:**
+Every applicant in HireHive should **contain a [unique](#glossary) name**! + - i.e. HireHive ensures that there are no applicants in HireHive that have the same name! + - More specifically, applicants can share the same phone number, email, address and role, but **not name**! + - Example: `add n/John Doe p/98765432 e/johnd@example.com a/311, Clementi Ave 2, #02-25 r/Software Engineer` and + `add n/John Wee p/98765432 e/johnd@example.com a/311, Clementi Ave 2, #02-25 r/Software Engineer` are treated as 2 different applicants and both applicants can be added! +
+ +
:bulb: **Tip:** +There is no need to manually add a tag as the `Applicant` tag is automatically assigned when you add a new applicant to HireHive. +
+ +Examples: +* `add n/Betsy Crowe e/betsycrowe@example.com a/Ang Mo Kio Street 22 p/87654321 r/senior consulting analyst`: +Adds an applicant with the name `Betsy Crowe`, who has `betsycrowe@example.com` as their email, `Ang Mo Kio Street 22` +as their address, `87654321` as their phone number, and is applying for the `senior consulting analyst` role + +![add message](images/Ui-AddCommand.png) + +[Back to top](#welcome-to-hirehive) + +### Edit an applicant: `edit` + +If you have accidentally mistyped some details, you can edit the information of an existing applicant in HireHive! + +Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG] [r/ROLE] [i/NOTE] [id/DATE]` + +* Edits the applicant at the specified `INDEX`. The index refers to the index number shown in the displayed applicant list. The index **must be a positive integer** 1, 2, 3, …​ +* At least one of the optional fields must be provided. +* The provided values will replace the existing information in the respective fields. + +
+ +**:information_source: Note:**
+* All the fields provided should follow the same rules as the [Add command](#add-an-applicant-add) +* Every applicant should have a **[unique](#glossary)** name +
+ + +Examples: +* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st applicant to be `91234567` and `johndoe@example.com` respectively. + +![edit message](images/Ui-EditCommand.png) + +[Back to top](#welcome-to-hirehive) + + +### Tag an applicant: `tag` -### Adding a person: `add` +You can change the tag to a specific applicant from HireHive to easily differentiate between all your applicants! -Adds a person to the address book. +Format: -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +Command | Description +--------|------------------ +`tag n/NAME t/TAG` | Tags by name. +`tag INDEX t/TAG` | Tags by index. +`tag OFFSET n/NAME` | Offset tag of named applicant.
:bulb: **Tip:** -A person can have any number of tags (including 0) +In the event where you are unable to select a candidate due to duplicated names, select them with their index instead!
+* Tagging by name will partially match to **exactly one applicant** in the list ([case-insensitive](#glossary)). + * If multiple matches are found, a list of all matching names will be displayed: + - You can then enter the **full name** of the applicant you want to tag +* Tagging by index must have a **positive** index number +* Tags must be one of the following values, which are colour-coded as follows: + 1.

Rejected

+ 2.

Applicant

+ 3.

Candidate

+ 4.

Interviewee

+ 5.

Offered

+* The tag written in the command must match one of the values above ([case-insensitive](#glossary)) e.g. `t/applicant` +* To tag with offset, the offset takes in `+` or `-`, followed by a number for the offset amount, e.g. `+1`, `-2` + * The tag, which represents the hiring stage the specific applicant is at, will progress/regress the hiring stage by + the offset amount + * The offset amount must range from -4 to +4 + * The order for the hiring stages is `Rejected` > `Applicant` > `Candidate` > `Interviewee` > `Offered` + * The tag will not regress past `Rejected` and progress past `Offered` + * For example, if you enter the command `tag -4 n/John` with an applicant named John tagged as `Applicant`, + John's tag is set to `Rejected` + + +Examples: +* `tag n/John Doe t/Applicant` will tag `John Doe` as `Applicant` in HireHive + +![tag message](images/Ui-TagCommand.png) + +* `tag 1 t/Candidate` will tag the 1st applicant as `Candidate` +* `tag +1 n/John Doe` for a `John Doe` applicant with tag `Applicant` will tag them as `Candidate` + +[Back to top](#welcome-to-hirehive) + + +### Add note to applicant: `newnote` + +You can add information about the applicant into a note with this command. + +Format: `newnote n/NAME i/NOTE` + +* Adds the note if the given name partially matches **exactly one** applicant in the list ([case-insensitive](#glossary)) +* If multiple matches are found, a list of all matching names will be displayed. You should then retry the command but using the **full name** of the desired applicant. +* NOTE has a limit of 500 characters. +* After adding, a popup window will appear containing the given input. +* If the input is empty or consists only of whitespace, then the popup window displays a default message "Currently empty..." in yellow. + 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` +* `newnote n/John Doe i/25 years old` adds a note to John Doe that says "25 years old" + +![newnote message](images/Ui-NewNoteCommand.png) + +* `newnote n/John Doe i/` removes any existing content in John Doe's note + +
:bulb: **Tip:** +You can remove the contents of the note by leaving the NOTE field blank. +
+ +[Back to top](#welcome-to-hirehive) + +### Schedule interview dates for applicants : `schedule` -### Listing all persons : `list` +Automatically or manually schedule your next interview with a potential candidate. -Shows a list of all persons in the address book. +Format: + +Command | Description +--------|------------------ +`schedule n/NAME [id/DATE]` | Schedule interview date by name. +`schedule INDEX [id/DATE]` | Schedule interview date by index. + +
:bulb: **Tip:** +In the event where you are unable to select a candidate due to duplicated names, select them with their index instead! +
+ +* The date format is DD/MM/YYYY (date/month/year) e.g. "04/04/2025" +* The command will only accept dates starting from the current date. +* If date is left empty, the next day that does not have an interview after the current day will be used to schedule the meeting instead. +* Scheduling by name will partially match to **exactly one applicant** in the list ([case-insensitive](#glossary)). + * If multiple matches are found, a list of all matching names will be displayed + * You can then enter the **full name** of the applicant you want to schedule +* You will have to provide a **positive [integer](#glossary)** to select a candidate by index. +* Scheduling an interview with an `Applicant` or `Candidate` will automatically change their tag to an `Interviewee`. + +
:exclamation: **Caution:** +You will not be allowed to schedule interviews with candidates that are already `Offered`/`Rejected`. Please change +their tags if you wish to schedule a second interview. +
+ +Examples: +* Running `schedule n/John Doe id/01/01/2026` will schedule an interview with John Doe on 01/01/2026 + +![schedule message](images/Ui-ScheduleCommand.png) + +* Running `schedule n/John Doe` on 01/01/2026 when there are no interviews scheduled will schedule an interview with + John Doe on 02/01/2026 +* Running `schedule n/John Doe` on 01/01/2026 when there is an interview on 02/01/2026 will instead schedule the + interview with John Doe on 03/01/2026 + +[Back to top](#welcome-to-hirehive) + +### Listing applicant information + +### Display note of applicant: `displaynote` + +You can display the note of an applicant with this command. + +Format: `displaynote n/NAME` + +* Displays the note if the given name partially matches **exactly one** applicant in the list ([case-insensitive](#glossary)) +* If multiple matches are found, a list of all matching names will be displayed. You should then retry the command but using the **full name** of the desired applicant. +* A popup window will appear containing the applicant's note. +* If the note is empty, then the popup window displays a default message "Currently empty..." in yellow. + +Examples: +* `displaynote n/John Doe` shows John Doe's note + +![displaynote message](images/Ui-DisplayNoteCommand.png) + +[Back to top](#welcome-to-hirehive) + +### Sort applicants: `sort` + +Sorts the applicants displayed on your screen by interview date, in chronological order so that you can conveniently view upcoming interviews. + +Format: `sort` + +![sort message](images/Ui-SortCommand.png) + +[Back to top](#welcome-to-hirehive) + +### List all applicants: `list` + +Shows you a list of all the applicants in HireHive so that you can look at an overview of all the applicants if needed. + +
:bulb: **Tip:** +This command also resets any filters applied by the commands below! +
Format: `list` -### Editing a person : `edit` +![list message](images/Ui-ListCommand.png) -Edits an existing person in the address book. +[Back to top](#welcome-to-hirehive) -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +### Filtering applicant information -* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ -* At least one of the optional fields must be provided. -* Existing values will be updated to the input values. -* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative. -* You can remove all the person’s tags by typing `t/` without - specifying any tags after it. +The commands below are accumulative! Using one filter command after another will apply **both** filters to the applicant list. The filters will persist until other commands are used. + +For example, running `filter t/interviewee` and `remind 5` will filter out all interviewees whose interviews are in 5 days. + +### Filter applicants by tag: `filter` + +You can view all your applicants with a specific tag for better organization! + +Format: `filter t/TAG` + +* The search is [case-insensitive](#glossary). e.g `applicant` will match `Applicant` +* Only full words will be matched e.g. `Reject` will not match `Rejected` + +Examples: +* `filter t/Applicant` will filter and display everyone with the `Applicant` tag in the list + +![filter message](images/Ui-FilterCommand.png) + +[Back to top](#welcome-to-hirehive) + +### Filtering out applicants with tag: `filterout` +You can view all your applicants in a list **except** for those with the specified tag, for easier tracking of applicants! + +Format: `filterout t/TAG` + +* The search is [case-insensitive](#glossary). e.g `applicant` will match `Applicant` 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. +* `filterout t/offered` will filter out all applicants that were already offered the job, the remaining list will + display applicants without the `Offered` tag. + +![filterout message](images/Ui-FilterOutCommand.png) + +[Back to top](#welcome-to-hirehive) + + +### Show applicants with upcoming interviews: `remind` -### Locating persons by name: `find` +Easily remind yourself of your upcoming interviews by listing down all applicants with interviews in the given days' time. -Finds persons whose names contain any of the given keywords. +Format: `remind DAYS` + +* `DAYS` is the number of upcoming days you wish to check for interviews for (inclusive). +* The number of days given must be a **non-negative [integer](#glossary)**, i.e. greater or equal to 0 + +Examples: + +Given that today is 08/04/2025, and you have the following interviews scheduled: + +- Alice Yeoh: 08/04/2025 +- Bernice Yu: 10/04/2025 +- Charlotte Oliveiro: 31/12/2025 + +* `remind 0` will display Alice's entry + +![remind message](images/Ui-RemindCommand.png) + +* `remind 2` will display Alice's and Bob's entry + +[Back to top](#welcome-to-hirehive) + +### Find applicants by name: `find` +You can search for applicants whose names contain any of the given keywords! Format: `find KEYWORD [MORE_KEYWORDS]` -* 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` +* The search follows partial name matching. e.g. `find Han` will return `Han` and `Hans` +* The search is [case-insensitive](#glossary). e.g. `hans` will match `Hans` +* The order of the keywords **matter**. e.g. `Hans Bo` will not match `Bo Hans` +* Only keywords in name are searched. + Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +* `find John` with an applicant John Doe in the list returns `John Doe` + +![find message](images/Ui-FindCommand.png) + +[Back to top](#welcome-to-hirehive) + +### Deletion of applicants + +### Delete an applicant: `delete` -### Deleting a person : `delete` +You can delete a specific applicant from HireHive if their contact is no longer required. -Deletes the specified person from the address book. +Format: -Format: `delete INDEX` +Command | Description +--------|------------------ +`delete n/NAME` | Delete by name. +`delete INDEX` | Delete by 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, …​ +
:bulb: **Tip:** +In the event where you are unable to select a candidate due to duplicated names, select them with their index instead! +
+ +* Deleting by name will partially match to **exactly one applicant** in the list ([case-insensitive](#glossary)). + * If multiple matches are found, a list of all matching names will be displayed + * You can then enter the **full name** of the applicant you want to delete +* Deleting by index must have a **positive** index number 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. +* `delete n/John Doe` deletes John Doe's contact from HireHive + +![delete message](images/Ui-DeleteCommand.png) -### Clearing all entries : `clear` +* `delete 2` deletes the second applicant in the list from HireHive -Clears all entries from the address book. +[Back to top](#welcome-to-hirehive) + +### Clear all entries : `clear` + +Clears all entries from HireHive. Format: `clear` -### Exiting the program : `exit` +
+ +:exclamation: **Caution:** +This action is **irreversible**! Please use this command with caution! +
+ +
+ +**:information_source: Note:**
+If you `clear` the data in HireHive and exit the app immediately, when you re-run HireHive, you will see a message on your screen warning you that the data might be corrupted. But not to worry! You may ignore this message and continue using HireHive as per normal. +
+ +![clear message](images/Ui-ClearCommand.png) + +[Back to top](#welcome-to-hirehive) -Exits the program. +### Exit the program : `exit` + +You can use this command to exit the program once you are done using it. Format: `exit` -### Saving the data +[Back to top](#welcome-to-hirehive) + +### Save the data + +HireHive data is saved in the [hard disk](#glossary) automatically after any command that changes the data. If successfully saved, the output following the command will display the success message. There is no need to save manually. -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +[Back to top](#welcome-to-hirehive) -### Editing the data file +### Edit the data file -AddressBook data are saved automatically as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file. +HireHive data are saved automatically as a [JSON](#glossary) file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file.
:exclamation: **Caution:** -If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
-Furthermore, certain edits can cause the AddressBook to behave in unexpected ways (e.g., if a value entered is outside of the acceptable range). Therefore, edit the data file only if you are confident that you can update it correctly. +If your changes to the data file makes its format **invalid**, HireHive will start with an **empty** data file at the next run. It will **discard** the old invalid data after the first successful command during this run. Hence, it is recommended to take a backup of the file before editing it.
+Furthermore, certain edits can cause HireHive to behave in unexpected ways (e.g., if a value entered is outside the acceptable range, or it is not of the expected data type). Therefore, edit the data file only if you are confident that you can update it correctly.
-### Archiving data files `[coming in v2.0]` - -_Details coming soon ..._ +[Back to top](#welcome-to-hirehive) -------------------------------------------------------------------------------------------------------------------- ## FAQ -**Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder. +**Q**: How do I transfer my data to another computer?
+**A**: Install the app in the other computer and overwrite the data file it creates with the file that contains the data of your previous HireHive home folder. + +[Back to top](#welcome-to-hirehive) -------------------------------------------------------------------------------------------------------------------- ## Known issues -1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again. -2. **If you minimize the Help Window** and then run the `help` command (or use the `Help` menu, or the keyboard shortcut `F1`) again, the original Help Window will remain minimized, and no new Help Window will appear. The remedy is to manually restore the minimized Help Window. +1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the [GUI](#glossary) will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again. +2. If you **minimize the Help Window and then run the `help` command** (or use the `Help` menu, or the keyboard [shortcut](#glossary) `F1`) again, the original Help Window will remain minimized, and no new Help Window will appear. The remedy is to manually restore the minimized Help Window. + +[Back to top](#welcome-to-hirehive) -------------------------------------------------------------------------------------------------------------------- -## Command summary +## Glossary -Action | Format, Examples +Term | Description --------|------------------ -**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` +**Alphanumeric** | Consisting of both letters (A to Z) and numbers (0 to 9). +**Applicant** | A person who makes a formal application for something, especially a job. +**Case-insensitive** | Uppercase and lowercase letters are treated the same and hence equivalent. +**Graphical User Interface (GUI)** | A form of user interface that allows users to interact with electronic devices through graphical icons and visual indicators such as secondary notation. +**Hard disk** | The storage device used by a computer. These can be used as primary or secondary storage. +**Integer** | A whole number within the range of -231 and 231 inclusive. +**JSON** | Acronym for _JavaScript Object Notation,_ an open standard file format and data interchange format that uses human-readable text to store and transmit data objects consisting of name–value pairs and arrays (or other serializable values). +**Shortcut** | A key or combination of keys that you can press on a computer keyboard to quickly perform a specific action. +**Unique** | Only 1 specific instance of it exists. + +[Back to top](#welcome-to-hirehive) + +-------------------------------------------------------------------------------------------------------------------- diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..bf406ecb908 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "HireHive" theme: minima header_pages: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..057c9210d37 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: "HireHive"; font-size: 32px; } } diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index 48b6cc4333c..7f15449f688 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -8,18 +8,23 @@ 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 n/Alice" activate ui UI_COLOR -ui -[UI_COLOR]> logic : execute("delete 1") +ui -[UI_COLOR]> logic : execute("delete n/Alice") activate logic LOGIC_COLOR logic -[LOGIC_COLOR]> model : deletePerson(p) activate model MODEL_COLOR model -[MODEL_COLOR]-> logic + +logic -[LOGIC_COLOR]> model : updateFilteredPersonList +model -[MODEL_COLOR]-> logic + deactivate model + logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook) activate storage STORAGE_COLOR diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml index 598474a5c82..51f21867e0d 100644 --- a/docs/diagrams/BetterModelClassDiagram.puml +++ b/docs/diagrams/BetterModelClassDiagram.puml @@ -18,4 +18,6 @@ Person *--> Name Person *--> Phone Person *--> Email Person *--> Address +Person *--> Role +Person *--> InterviewDate @enduml diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 5241e79d7da..54d875e1ace 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -6,20 +6,28 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR +participant ":ArgumentTokenizer" as ArgumentTokenizer LOGIC_COLOR +participant ":ArgumentMultimap" as ArgumentMultimap LOGIC_COLOR +participant "q:NameQuery" as NameQuery LOGIC_COLOR participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR +participant "<> CommandUtil" as CommandUtil LOGIC_COLOR +participant "l:List" as ListPerson LOGIC_COLOR participant "r:CommandResult" as CommandResult LOGIC_COLOR end box box Model MODEL_COLOR_T1 participant "m:Model" as Model MODEL_COLOR +participant "n:NameContainsKeywordsPredicate" as NameContainsKeywordsPredicate MODEL_COLOR end box -[-> LogicManager : execute("delete 1") +[-> LogicManager : execute("delete n/Alice") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") +LogicManager -> AddressBookParser : parseCommand("delete n/Alice") activate AddressBookParser +create CommandUtil + create DeleteCommandParser AddressBookParser -> DeleteCommandParser activate DeleteCommandParser @@ -27,14 +35,43 @@ activate DeleteCommandParser DeleteCommandParser --> AddressBookParser deactivate DeleteCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") +AddressBookParser -> DeleteCommandParser : parse("n/Alice") activate DeleteCommandParser +create ArgumentTokenizer +DeleteCommandParser -> ArgumentTokenizer : tokenize("n/Alice", PREFIX_NAME) +activate ArgumentTokenizer + +create ArgumentMultimap +ArgumentTokenizer -> ArgumentMultimap : extractArguments +activate ArgumentMultimap +ArgumentMultimap --> ArgumentTokenizer +deactivate ArgumentMultimap +ArgumentTokenizer --> DeleteCommandParser +deactivate ArgumentTokenizer + +DeleteCommandParser -> ArgumentMultimap : getValue(PREFIX_NAME) +activate ArgumentMultimap +ArgumentMultimap --> DeleteCommandParser : "Alice" +deactivate ArgumentMultimap + +create NameContainsKeywordsPredicate +DeleteCommandParser -> NameContainsKeywordsPredicate : new NameContainsKeywordsPredicate("Alice") +activate NameContainsKeywordsPredicate +NameContainsKeywordsPredicate --> DeleteCommandParser : n +deactivate NameContainsKeywordsPredicate + +create NameQuery +DeleteCommandParser -> NameQuery : new(n) +activate NameQuery +NameQuery --> DeleteCommandParser : q +deactivate NameQuery + create DeleteCommand -DeleteCommandParser -> DeleteCommand +DeleteCommandParser -> DeleteCommand : new DeleteCommand(q) activate DeleteCommand -DeleteCommand --> DeleteCommandParser : +DeleteCommand --> DeleteCommandParser : d deactivate DeleteCommand DeleteCommandParser --> AddressBookParser : d @@ -49,9 +86,33 @@ deactivate AddressBookParser LogicManager -> DeleteCommand : execute(m) activate DeleteCommand -DeleteCommand -> Model : deletePerson(1) +DeleteCommand -> CommandUtil : querySearch(m, q) +activate CommandUtil + +CommandUtil -> NameQuery : query(m) +activate NameQuery + +NameQuery -> Model : query(m) activate Model +Model -> NameContainsKeywordsPredicate : updateFilteredPersonList +activate NameContainsKeywordsPredicate + +NameContainsKeywordsPredicate --> Model +deactivate NameContainsKeywordsPredicate + +Model --> NameQuery : List l +deactivate Model + +NameQuery -> CommandUtil : List l +deactivate NameQuery +CommandUtil -> DeleteCommand : Person : p +deactivate CommandUtil + +DeleteCommand -> Model : deletePerson(p) +activate Model +Model --> DeleteCommand +DeleteCommand -> Model : updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS) Model --> DeleteCommand deactivate Model diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 0de5673070d..7af200a0772 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -19,6 +19,10 @@ Class Email Class Name Class Phone Class Tag +Class Note +Class Role +Class InterviewDate + Class I #FFFFFF } @@ -37,18 +41,20 @@ UserPrefs .up.|> ReadOnlyUserPrefs AddressBook *--> "1" UniquePersonList UniquePersonList --> "~* all" Person -Person *--> Name +Person *-left-> Name Person *--> Phone -Person *--> Email +Person *-right-> Email Person *--> Address -Person *--> "*" Tag +Person *--> Tag +Person *--> Role +Person *--> Note +Person *--> InterviewDate Person -[hidden]up--> I UniquePersonList -[hidden]right-> I -Name -[hidden]right-> Phone -Phone -[hidden]right-> Address -Address -[hidden]right-> Email +Name -[hidden]down-> Address +Email -[hidden]down-> InterviewDate ModelManager --> "~* filtered" Person @enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 95473d5aa19..cbe4be4c373 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 NoteWindow 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" NoteWindow PersonListPanel -down-> "*" PersonCard @@ -46,6 +48,7 @@ PersonListPanel --|> UiPart PersonCard --|> UiPart StatusBarFooter --|> UiPart HelpWindow --|> UiPart +NoteWindow --|> UiPart PersonCard ..> Model UiManager -right-> Logic diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png index 02a42e35e76..ecfc59a223a 100644 Binary files a/docs/images/BetterModelClassDiagram.png and b/docs/images/BetterModelClassDiagram.png differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index ac2ae217c51..b061d95d583 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index a19fb1b4ac8..bf5a716b7cf 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/Ui-2.png b/docs/images/Ui-2.png new file mode 100644 index 00000000000..fd60292788b Binary files /dev/null and b/docs/images/Ui-2.png differ diff --git a/docs/images/Ui-AddCommand.png b/docs/images/Ui-AddCommand.png new file mode 100644 index 00000000000..283638f200f Binary files /dev/null and b/docs/images/Ui-AddCommand.png differ diff --git a/docs/images/Ui-ClearCommand.png b/docs/images/Ui-ClearCommand.png new file mode 100644 index 00000000000..02d24b0194b Binary files /dev/null and b/docs/images/Ui-ClearCommand.png differ diff --git a/docs/images/Ui-DeleteCommand.png b/docs/images/Ui-DeleteCommand.png new file mode 100644 index 00000000000..cef07afc69e Binary files /dev/null and b/docs/images/Ui-DeleteCommand.png differ diff --git a/docs/images/Ui-DisplayNoteCommand.png b/docs/images/Ui-DisplayNoteCommand.png new file mode 100644 index 00000000000..578edcc212f Binary files /dev/null and b/docs/images/Ui-DisplayNoteCommand.png differ diff --git a/docs/images/Ui-EditCommand.png b/docs/images/Ui-EditCommand.png new file mode 100644 index 00000000000..072c5c5a0cf Binary files /dev/null and b/docs/images/Ui-EditCommand.png differ diff --git a/docs/images/Ui-FilterCommand.png b/docs/images/Ui-FilterCommand.png new file mode 100644 index 00000000000..0ead25717b7 Binary files /dev/null and b/docs/images/Ui-FilterCommand.png differ diff --git a/docs/images/Ui-FilterOutCommand.png b/docs/images/Ui-FilterOutCommand.png new file mode 100644 index 00000000000..80779a7a89e Binary files /dev/null and b/docs/images/Ui-FilterOutCommand.png differ diff --git a/docs/images/Ui-FindCommand.png b/docs/images/Ui-FindCommand.png new file mode 100644 index 00000000000..9ae5ede85e3 Binary files /dev/null and b/docs/images/Ui-FindCommand.png differ diff --git a/docs/images/Ui-ListCommand.png b/docs/images/Ui-ListCommand.png new file mode 100644 index 00000000000..c9b25187e8f Binary files /dev/null and b/docs/images/Ui-ListCommand.png differ diff --git a/docs/images/Ui-NewNoteCommand.png b/docs/images/Ui-NewNoteCommand.png new file mode 100644 index 00000000000..fc00284e7bc Binary files /dev/null and b/docs/images/Ui-NewNoteCommand.png differ diff --git a/docs/images/Ui-RemindCommand.png b/docs/images/Ui-RemindCommand.png new file mode 100644 index 00000000000..9f175779a77 Binary files /dev/null and b/docs/images/Ui-RemindCommand.png differ diff --git a/docs/images/Ui-ScheduleCommand.png b/docs/images/Ui-ScheduleCommand.png new file mode 100644 index 00000000000..9aeded40079 Binary files /dev/null and b/docs/images/Ui-ScheduleCommand.png differ diff --git a/docs/images/Ui-SortCommand.png b/docs/images/Ui-SortCommand.png new file mode 100644 index 00000000000..73c8c0332ab Binary files /dev/null and b/docs/images/Ui-SortCommand.png differ diff --git a/docs/images/Ui-TagCommand.png b/docs/images/Ui-TagCommand.png new file mode 100644 index 00000000000..fa0366e414e Binary files /dev/null and b/docs/images/Ui-TagCommand.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5bd77847aa2..cdf9bf2bee8 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png index 11f06d68671..5a69fd0cdb0 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/UpdatedArchitectureSequenceDiagram.png b/docs/images/UpdatedArchitectureSequenceDiagram.png new file mode 100644 index 00000000000..e9e7aaf458b Binary files /dev/null and b/docs/images/UpdatedArchitectureSequenceDiagram.png differ diff --git a/docs/images/deannapoh.png b/docs/images/deannapoh.png new file mode 100644 index 00000000000..d6587ed8950 Binary files /dev/null and b/docs/images/deannapoh.png differ diff --git a/docs/images/ditzchann.png b/docs/images/ditzchann.png new file mode 100644 index 00000000000..13e5190e0b7 Binary files /dev/null and b/docs/images/ditzchann.png differ diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png index b1f70470137..6f74992dc6c 100644 Binary files a/docs/images/helpMessage.png and b/docs/images/helpMessage.png differ diff --git a/docs/images/m0destly.png b/docs/images/m0destly.png new file mode 100644 index 00000000000..bcd29edd143 Binary files /dev/null and b/docs/images/m0destly.png differ diff --git a/docs/images/nat-ong555.png b/docs/images/nat-ong555.png new file mode 100644 index 00000000000..76c92213cc6 Binary files /dev/null and b/docs/images/nat-ong555.png differ diff --git a/docs/images/zeotheburrito.png b/docs/images/zeotheburrito.png new file mode 100644 index 00000000000..bafb635d16a Binary files /dev/null and b/docs/images/zeotheburrito.png differ diff --git a/docs/img.png b/docs/img.png new file mode 100644 index 00000000000..0bcd79ea04b Binary files /dev/null and b/docs/img.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..93c9bc408b9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ --- layout: page -title: AddressBook Level-3 +title: HireHive --- [![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) @@ -8,10 +8,11 @@ title: AddressBook Level-3 ![Ui](images/Ui.png) -**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). +HireHive is your **personal, all-in-one** hiring assistant designed to simplify the hiring process so you can focus on what matters most - finding the best talent. -* 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 HireHive, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). +* If you are interested about developing HireHive, the [**Developer Guide**](DeveloperGuide.html) is a good place to + start. **Acknowledgements** diff --git a/docs/team/deannapoh.md b/docs/team/deannapoh.md new file mode 100644 index 00000000000..c7bbe934948 --- /dev/null +++ b/docs/team/deannapoh.md @@ -0,0 +1,46 @@ +--- +layout: page +title: Deanna's Project Portfolio Page +--- + +### Project: HireHive + +Hirehive is a applicant tracking system (ATS) designed to help businesses streamline their recruitment process. It allows companies to post job listings, track applicants, manage interviews, and collaborate with hiring teams in one centralized platform. + +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/ditzchann.md b/docs/team/ditzchann.md new file mode 100644 index 00000000000..dc858bf065a --- /dev/null +++ b/docs/team/ditzchann.md @@ -0,0 +1,4 @@ +--- +layout: page +title: Ditzchann's Project Portfolio Page +--- diff --git a/docs/team/m0destly.md b/docs/team/m0destly.md new file mode 100644 index 00000000000..2e8aca7e30d --- /dev/null +++ b/docs/team/m0destly.md @@ -0,0 +1,4 @@ +--- +layout: page +title: m0destly's Project Portfolio Page +--- diff --git a/docs/team/nat-ong555.md b/docs/team/nat-ong555.md new file mode 100644 index 00000000000..0df2c32a8c5 --- /dev/null +++ b/docs/team/nat-ong555.md @@ -0,0 +1,8 @@ +--- +layout: page +title: Natalie's Project Portfolio Page +--- + +### Project: HireHive + +HireHive is a desktop address book application used by hiring managers to track candidate profiles and interviews. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. diff --git a/docs/team/zeotheburrito.md b/docs/team/zeotheburrito.md new file mode 100644 index 00000000000..231f339e32d --- /dev/null +++ b/docs/team/zeotheburrito.md @@ -0,0 +1,4 @@ +--- +layout: page +title: Wong Zenwei's Project Portfolio Page +--- diff --git a/src/main/java/seedu/address/AppParameters.java b/src/main/java/hirehive/address/AppParameters.java similarity index 91% rename from src/main/java/seedu/address/AppParameters.java rename to src/main/java/hirehive/address/AppParameters.java index 3d603622d4e..5b5c9d2747c 100644 --- a/src/main/java/seedu/address/AppParameters.java +++ b/src/main/java/hirehive/address/AppParameters.java @@ -1,4 +1,4 @@ -package seedu.address; +package hirehive.address; import java.nio.file.Path; import java.nio.file.Paths; @@ -6,10 +6,10 @@ import java.util.Objects; import java.util.logging.Logger; +import hirehive.address.commons.core.LogsCenter; +import hirehive.address.commons.util.FileUtil; +import hirehive.address.commons.util.ToStringBuilder; import javafx.application.Application; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.FileUtil; -import seedu.address.commons.util.ToStringBuilder; /** * Represents the parsed command-line parameters given to the application. diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/hirehive/address/Main.java similarity index 95% rename from src/main/java/seedu/address/Main.java rename to src/main/java/hirehive/address/Main.java index 9461d6da769..e5afae52614 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/hirehive/address/Main.java @@ -1,9 +1,9 @@ -package seedu.address; +package hirehive.address; import java.util.logging.Logger; +import hirehive.address.commons.core.LogsCenter; import javafx.application.Application; -import seedu.address.commons.core.LogsCenter; /** * The main entry point to the application. diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/hirehive/address/MainApp.java similarity index 84% rename from src/main/java/seedu/address/MainApp.java rename to src/main/java/hirehive/address/MainApp.java index 678ddc8c218..9b9e409e576 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/hirehive/address/MainApp.java @@ -1,42 +1,41 @@ -package seedu.address; +package hirehive.address; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; import java.util.logging.Logger; +import hirehive.address.commons.core.Config; +import hirehive.address.commons.core.LogsCenter; +import hirehive.address.commons.core.Version; +import hirehive.address.commons.exceptions.DataLoadingException; +import hirehive.address.commons.util.ConfigUtil; +import hirehive.address.commons.util.StringUtil; +import hirehive.address.logic.Logic; +import hirehive.address.logic.LogicManager; +import hirehive.address.model.AddressBook; +import hirehive.address.model.Model; +import hirehive.address.model.ModelManager; +import hirehive.address.model.ReadOnlyAddressBook; +import hirehive.address.model.ReadOnlyUserPrefs; +import hirehive.address.model.UserPrefs; +import hirehive.address.model.util.SampleDataUtil; +import hirehive.address.storage.AddressBookStorage; +import hirehive.address.storage.JsonAddressBookStorage; +import hirehive.address.storage.JsonUserPrefsStorage; +import hirehive.address.storage.Storage; +import hirehive.address.storage.StorageManager; +import hirehive.address.storage.UserPrefsStorage; +import hirehive.address.ui.Ui; +import hirehive.address.ui.UiManager; 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.DataLoadingException; -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, 2, true); + public static final Version VERSION = new Version(1, 3, 0, true); private static final Logger logger = LogsCenter.getLogger(MainApp.class); diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/hirehive/address/commons/core/Config.java similarity index 94% rename from src/main/java/seedu/address/commons/core/Config.java rename to src/main/java/hirehive/address/commons/core/Config.java index 485f85a5e05..2bac71518ab 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/hirehive/address/commons/core/Config.java @@ -1,11 +1,11 @@ -package seedu.address.commons.core; +package hirehive.address.commons.core; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Objects; import java.util.logging.Level; -import seedu.address.commons.util.ToStringBuilder; +import hirehive.address.commons.util.ToStringBuilder; /** * Config values used by the app diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/hirehive/address/commons/core/GuiSettings.java similarity index 96% rename from src/main/java/seedu/address/commons/core/GuiSettings.java rename to src/main/java/hirehive/address/commons/core/GuiSettings.java index a97a86ee8d7..135586b7040 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/hirehive/address/commons/core/GuiSettings.java @@ -1,10 +1,10 @@ -package seedu.address.commons.core; +package hirehive.address.commons.core; import java.awt.Point; import java.io.Serializable; import java.util.Objects; -import seedu.address.commons.util.ToStringBuilder; +import hirehive.address.commons.util.ToStringBuilder; /** * A Serializable class that contains the GUI settings. diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/hirehive/address/commons/core/LogsCenter.java similarity index 99% rename from src/main/java/seedu/address/commons/core/LogsCenter.java rename to src/main/java/hirehive/address/commons/core/LogsCenter.java index 8cf8e15a0f0..4973a9b4bbc 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/hirehive/address/commons/core/LogsCenter.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package hirehive.address.commons.core; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/hirehive/address/commons/core/Version.java similarity index 98% rename from src/main/java/seedu/address/commons/core/Version.java rename to src/main/java/hirehive/address/commons/core/Version.java index 491d24559b4..32000caa181 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/hirehive/address/commons/core/Version.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package hirehive.address.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/hirehive/address/commons/core/index/Index.java similarity index 94% rename from src/main/java/seedu/address/commons/core/index/Index.java rename to src/main/java/hirehive/address/commons/core/index/Index.java index dd170d8b68d..02b438cfd56 100644 --- a/src/main/java/seedu/address/commons/core/index/Index.java +++ b/src/main/java/hirehive/address/commons/core/index/Index.java @@ -1,6 +1,6 @@ -package seedu.address.commons.core.index; +package hirehive.address.commons.core.index; -import seedu.address.commons.util.ToStringBuilder; +import hirehive.address.commons.util.ToStringBuilder; /** * Represents a zero-based or one-based index. diff --git a/src/main/java/seedu/address/commons/exceptions/DataLoadingException.java b/src/main/java/hirehive/address/commons/exceptions/DataLoadingException.java similarity index 81% rename from src/main/java/seedu/address/commons/exceptions/DataLoadingException.java rename to src/main/java/hirehive/address/commons/exceptions/DataLoadingException.java index 9904ba47afe..f65b5ca4482 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataLoadingException.java +++ b/src/main/java/hirehive/address/commons/exceptions/DataLoadingException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package hirehive.address.commons.exceptions; /** * Represents an error during loading of data from a file. diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/hirehive/address/commons/exceptions/IllegalValueException.java similarity index 92% rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java rename to src/main/java/hirehive/address/commons/exceptions/IllegalValueException.java index 19124db485c..7e28f01a6f6 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/hirehive/address/commons/exceptions/IllegalValueException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package hirehive.address.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/hirehive/address/commons/util/AppUtil.java similarity index 94% rename from src/main/java/seedu/address/commons/util/AppUtil.java rename to src/main/java/hirehive/address/commons/util/AppUtil.java index 87aa89c0326..de1873f206c 100644 --- a/src/main/java/seedu/address/commons/util/AppUtil.java +++ b/src/main/java/hirehive/address/commons/util/AppUtil.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package hirehive.address.commons.util; import static java.util.Objects.requireNonNull; +import hirehive.address.MainApp; import javafx.scene.image.Image; -import seedu.address.MainApp; /** * A container for App specific utility functions diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/hirehive/address/commons/util/CollectionUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/CollectionUtil.java rename to src/main/java/hirehive/address/commons/util/CollectionUtil.java index eafe4dfd681..525bd5eebee 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/hirehive/address/commons/util/CollectionUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package hirehive.address.commons.util; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/hirehive/address/commons/util/ConfigUtil.java similarity index 76% rename from src/main/java/seedu/address/commons/util/ConfigUtil.java rename to src/main/java/hirehive/address/commons/util/ConfigUtil.java index 7b829c3c4cc..59b8c1f5727 100644 --- a/src/main/java/seedu/address/commons/util/ConfigUtil.java +++ b/src/main/java/hirehive/address/commons/util/ConfigUtil.java @@ -1,11 +1,11 @@ -package seedu.address.commons.util; +package hirehive.address.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.DataLoadingException; +import hirehive.address.commons.core.Config; +import hirehive.address.commons.exceptions.DataLoadingException; /** * A class for accessing the Config File. diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/hirehive/address/commons/util/FileUtil.java similarity index 98% rename from src/main/java/seedu/address/commons/util/FileUtil.java rename to src/main/java/hirehive/address/commons/util/FileUtil.java index b1e2767cdd9..1665ed9ebff 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/hirehive/address/commons/util/FileUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package hirehive.address.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/hirehive/address/commons/util/JsonUtil.java similarity index 97% rename from src/main/java/seedu/address/commons/util/JsonUtil.java rename to src/main/java/hirehive/address/commons/util/JsonUtil.java index 100cb16c395..47dc262ea2a 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/hirehive/address/commons/util/JsonUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package hirehive.address.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.DataLoadingException; +import hirehive.address.commons.core.LogsCenter; +import hirehive.address.commons.exceptions.DataLoadingException; /** * 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/hirehive/address/commons/util/StringUtil.java similarity index 58% rename from src/main/java/seedu/address/commons/util/StringUtil.java rename to src/main/java/hirehive/address/commons/util/StringUtil.java index 61cc8c9a1cb..8f5dc2f4dbe 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/hirehive/address/commons/util/StringUtil.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package hirehive.address.commons.util; +import static hirehive.address.commons.util.AppUtil.checkArgument; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; import java.io.PrintWriter; import java.io.StringWriter; @@ -29,13 +29,8 @@ public static boolean containsWordIgnoreCase(String sentence, String word) { String preppedWord = word.trim(); checkArgument(!preppedWord.isEmpty(), "Word parameter cannot be empty"); - checkArgument(preppedWord.split("\\s+").length == 1, "Word parameter should be a single word"); - String preppedSentence = sentence; - String[] wordsInPreppedSentence = preppedSentence.split("\\s+"); - - return Arrays.stream(wordsInPreppedSentence) - .anyMatch(preppedWord::equalsIgnoreCase); + return sentence.toLowerCase().contains(preppedWord.toLowerCase()); } /** @@ -65,4 +60,40 @@ public static boolean isNonZeroUnsignedInteger(String s) { return false; } } + + /** + * Returns true if {@code s} represents a 0 or positive integer + * e.g. 0, 1, 2, 3, ..., {@code Integer.MAX_VALUE}
+ * Will return false for any other non-null string input + * e.g. empty string, "-1", "+1", and " 2 " (untrimmed), "3 0" (contains whitespace), "1 a" (contains letters) + * @throws NullPointerException if {@code s} is null. + */ + public static boolean isPositiveInteger(String s) { + requireNonNull(s); + + try { + int value = Integer.parseInt(s); + return value >= 0 && !s.startsWith("+"); // "+1" is successfully parsed by Integer#parseInt(String) + } catch (NumberFormatException nfe) { + return false; + } + } + + /** + * Returns true if {@code s} represents a string or a valid integer + * @throws NullPointerException if {@code s} is null. + */ + public static boolean isValidStringOrInteger(String s) { + requireNonNull(s); + if (s.matches("-?\\d+")) { + try { + long num = Long.parseLong(s); + return num >= Integer.MIN_VALUE && num <= Integer.MAX_VALUE; + } catch (NumberFormatException e) { + return false; + } + } else { + return true; + } + } } diff --git a/src/main/java/seedu/address/commons/util/ToStringBuilder.java b/src/main/java/hirehive/address/commons/util/ToStringBuilder.java similarity index 97% rename from src/main/java/seedu/address/commons/util/ToStringBuilder.java rename to src/main/java/hirehive/address/commons/util/ToStringBuilder.java index d979b926734..c1304e23538 100644 --- a/src/main/java/seedu/address/commons/util/ToStringBuilder.java +++ b/src/main/java/hirehive/address/commons/util/ToStringBuilder.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package hirehive.address.commons.util; /** * Builds a string representation of an object that is suitable as the return value of {@link Object#toString()}. diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/hirehive/address/logic/Logic.java similarity index 66% rename from src/main/java/seedu/address/logic/Logic.java rename to src/main/java/hirehive/address/logic/Logic.java index 92cd8fa605a..f5023654ec8 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/hirehive/address/logic/Logic.java @@ -1,14 +1,16 @@ -package seedu.address.logic; +package hirehive.address.logic; import java.nio.file.Path; +import hirehive.address.commons.core.GuiSettings; +import hirehive.address.logic.commands.CommandResult; +import hirehive.address.logic.commands.exceptions.CommandException; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.Model; +import hirehive.address.model.ReadOnlyAddressBook; +import hirehive.address.model.person.Note; +import hirehive.address.model.person.Person; 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 @@ -26,7 +28,7 @@ public interface Logic { /** * Returns the AddressBook. * - * @see seedu.address.model.Model#getAddressBook() + * @see Model#getAddressBook() */ ReadOnlyAddressBook getAddressBook(); @@ -47,4 +49,7 @@ public interface Logic { * Set the user prefs' GUI settings. */ void setGuiSettings(GuiSettings guiSettings); + + Note getPersonNote(); + int getFilteredPersonListSize(); } diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/hirehive/address/logic/LogicManager.java similarity index 74% rename from src/main/java/seedu/address/logic/LogicManager.java rename to src/main/java/hirehive/address/logic/LogicManager.java index 5aa3b91c7d0..f92a542048e 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/hirehive/address/logic/LogicManager.java @@ -1,22 +1,23 @@ -package seedu.address.logic; +package hirehive.address.logic; import java.io.IOException; import java.nio.file.AccessDeniedException; import java.nio.file.Path; import java.util.logging.Logger; +import hirehive.address.commons.core.GuiSettings; +import hirehive.address.commons.core.LogsCenter; +import hirehive.address.logic.commands.Command; +import hirehive.address.logic.commands.CommandResult; +import hirehive.address.logic.commands.exceptions.CommandException; +import hirehive.address.logic.parser.AddressBookParser; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.Model; +import hirehive.address.model.ReadOnlyAddressBook; +import hirehive.address.model.person.Note; +import hirehive.address.model.person.Person; +import hirehive.address.storage.Storage; 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. @@ -85,4 +86,13 @@ public GuiSettings getGuiSettings() { public void setGuiSettings(GuiSettings guiSettings) { model.setGuiSettings(guiSettings); } + + @Override + public Note getPersonNote() { + return model.getPersonNote(); + } + @Override + public int getFilteredPersonListSize() { + return model.getListSize(); + } } diff --git a/src/main/java/hirehive/address/logic/Messages.java b/src/main/java/hirehive/address/logic/Messages.java new file mode 100644 index 00000000000..8faf51aa1f1 --- /dev/null +++ b/src/main/java/hirehive/address/logic/Messages.java @@ -0,0 +1,76 @@ +package hirehive.address.logic; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import hirehive.address.logic.parser.Prefix; +import hirehive.address.model.person.Person; + +/** + * Container for user visible messages. + */ +public class Messages { + + public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; + public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; + public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The provided index is invalid. "; + public static final String MESSAGE_INDEX_OUT_OF_BOUNDS = "The provided index is out of bounds." + + "\n Please enter a number between 1 and %d"; + public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; + public static final String MESSAGE_DUPLICATE_FIELDS = + "Multiple values specified for the following single-valued field(s): "; + public static final String MESSAGE_NO_SUCH_PERSON = "No such person exists."; + + public static final String MESSAGE_MULTIPLE_PEOPLE_QUERIED = "There are multiple contacts containing the given name." + + "\n Please enter the full name of the person, or use their index if other people have the same name"; + + public static final String MESSAGE_MULTIPLE_PEOPLE_QUERIED_NAME = "There are multiple contacts containing the given name." + + "\n Please enter the full name of the person."; + + public static final String MESSAGE_FILTER_OVERVIEW_TAG = "Showing %s entries."; + public static final String MESSAGE_FILTER_OVERVIEW_DATE = "Showing entries with interviews in %1$d days."; + public static final String MESSAGE_FILTER_OVERVIEW_NAME = "Showing entries with keywords %s in name."; + public static final String MESSAGE_FILTEROUT_OVERVIEW_TAG = "Showing all entries without %s tag."; + public static final String MESSAGE_EMPTY_ADDRESS_BOOK = "Current address book is empty. This might be due to corrupted data." + + "\nWARNING: Please check if data/addressbook.json has old corrupted data and attempt to fix it, otherwise any new successful commands will overwrite those contents."; + public static final String MESSAGE_SAMPLE_ADDRESS_BOOK = "Success: Sample applicant data has been loaded successfully."; + public static final String MESSAGE_LOAD_SUCCESS = "Success: Applicant data has been loaded successfully."; + public static final String MESSAGE_DATA_SAVED = "\nSuccess: Applicant data has been saved."; + + /** + * Returns an error message indicating the duplicate prefixes. + */ + public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePrefixes) { + assert duplicatePrefixes.length > 0; + + Set duplicateFields = + Stream.of(duplicatePrefixes).map(Prefix::toString).collect(Collectors.toSet()); + + return MESSAGE_DUPLICATE_FIELDS + String.join(" ", duplicateFields); + } + + /** + * Formats the {@code person} for display to the user. + */ + public static String format(Person person) { + final StringBuilder builder = new StringBuilder(); + builder.append(person.getName()) + .append("; Phone: ") + .append(person.getPhone()) + .append("; Email: ") + .append(person.getEmail()) + .append("; Address: ") + .append(person.getAddress()) + .append("; Role: ") + .append(person.getRole()) + .append("; Date: ") + .append(person.getDate()) + .append("; Tag: ") + .append(person.getTag()) + .append("; Note: ") + .append(person.getNote().isEmpty() ? "Empty" : "Not empty"); + return builder.toString(); + } + +} diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/hirehive/address/logic/commands/AddCommand.java similarity index 68% rename from src/main/java/seedu/address/logic/commands/AddCommand.java rename to src/main/java/hirehive/address/logic/commands/AddCommand.java index 5d7185a9680..11d4314d65b 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/hirehive/address/logic/commands/AddCommand.java @@ -1,17 +1,19 @@ -package seedu.address.logic.commands; +package hirehive.address.logic.commands; +import static hirehive.address.logic.Messages.MESSAGE_DATA_SAVED; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NAME; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_ROLE; 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.commons.util.ToStringBuilder; -import seedu.address.logic.Messages; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; + +import hirehive.address.commons.util.ToStringBuilder; +import hirehive.address.logic.Messages; +import hirehive.address.logic.commands.exceptions.CommandException; +import hirehive.address.model.Model; +import hirehive.address.model.person.Person; + /** * Adds a person to the address book. @@ -20,23 +22,22 @@ 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. " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to HireHive.\n" + "Parameters: " + PREFIX_NAME + "NAME " + PREFIX_PHONE + "PHONE " + PREFIX_EMAIL + "EMAIL " + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" + + PREFIX_ROLE + "ROLE\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"; + + PREFIX_ROLE + "Software Engineer"; public static final String MESSAGE_SUCCESS = "New person added: %1$s"; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; + public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in HireHive"; private final Person toAdd; @@ -57,7 +58,7 @@ public CommandResult execute(Model model) throws CommandException { } model.addPerson(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(toAdd))); + return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(toAdd)), true); } @Override diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/hirehive/address/logic/commands/ClearCommand.java similarity index 63% rename from src/main/java/seedu/address/logic/commands/ClearCommand.java rename to src/main/java/hirehive/address/logic/commands/ClearCommand.java index 9c86b1fa6e4..6d743aa33b4 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/hirehive/address/logic/commands/ClearCommand.java @@ -1,9 +1,10 @@ -package seedu.address.logic.commands; +package hirehive.address.logic.commands; +import static hirehive.address.logic.Messages.MESSAGE_DATA_SAVED; import static java.util.Objects.requireNonNull; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; +import hirehive.address.model.AddressBook; +import hirehive.address.model.Model; /** * Clears the address book. @@ -18,6 +19,6 @@ public class ClearCommand extends Command { public CommandResult execute(Model model) { requireNonNull(model); model.setAddressBook(new AddressBook()); - return new CommandResult(MESSAGE_SUCCESS); + return new CommandResult(MESSAGE_SUCCESS, true); } } diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/hirehive/address/logic/commands/Command.java similarity index 77% rename from src/main/java/seedu/address/logic/commands/Command.java rename to src/main/java/hirehive/address/logic/commands/Command.java index 64f18992160..28d0a55a501 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/hirehive/address/logic/commands/Command.java @@ -1,7 +1,7 @@ -package seedu.address.logic.commands; +package hirehive.address.logic.commands; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; +import hirehive.address.logic.commands.exceptions.CommandException; +import hirehive.address.model.Model; /** * Represents a command with hidden internal logic and the ability to be executed. diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/hirehive/address/logic/commands/CommandResult.java similarity index 65% rename from src/main/java/seedu/address/logic/commands/CommandResult.java rename to src/main/java/hirehive/address/logic/commands/CommandResult.java index 249b6072d0d..0d9ea2b0c1f 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/hirehive/address/logic/commands/CommandResult.java @@ -1,10 +1,10 @@ -package seedu.address.logic.commands; +package hirehive.address.logic.commands; import static java.util.Objects.requireNonNull; import java.util.Objects; -import seedu.address.commons.util.ToStringBuilder; +import hirehive.address.commons.util.ToStringBuilder; /** * Represents the result of a command execution. @@ -19,13 +19,21 @@ public class CommandResult { /** The application should exit. */ private final boolean exit; + /** Note window should be displayed to the user. */ + private final boolean showNote; + + /** Saved message should appear to the user */ + private final boolean isChange; + /** * 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 showNote, boolean isChange) { this.feedbackToUser = requireNonNull(feedbackToUser); this.showHelp = showHelp; this.exit = exit; + this.showNote = showNote; + this.isChange = isChange; } /** @@ -33,7 +41,11 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { * and other fields set to their default value. */ public CommandResult(String feedbackToUser) { - this(feedbackToUser, false, false); + this(feedbackToUser, false, false, false, false); + } + + public CommandResult(String feedbackToUser, boolean isChange) { + this(feedbackToUser, false, false, false, isChange); } public String getFeedbackToUser() { @@ -48,6 +60,14 @@ public boolean isExit() { return exit; } + public boolean isShowNote() { + return showNote; + } + + public boolean isChange() { + return isChange; + } + @Override public boolean equals(Object other) { if (other == this) { @@ -62,7 +82,9 @@ public boolean equals(Object other) { CommandResult otherCommandResult = (CommandResult) other; return feedbackToUser.equals(otherCommandResult.feedbackToUser) && showHelp == otherCommandResult.showHelp - && exit == otherCommandResult.exit; + && exit == otherCommandResult.exit + && showNote == otherCommandResult.showNote + && isChange == otherCommandResult.isChange; } @Override @@ -76,6 +98,8 @@ public String toString() { .add("feedbackToUser", feedbackToUser) .add("showHelp", showHelp) .add("exit", exit) + .add("showNote", showNote) + .add("isChange", isChange) .toString(); } diff --git a/src/main/java/hirehive/address/logic/commands/CommandUtil.java b/src/main/java/hirehive/address/logic/commands/CommandUtil.java new file mode 100644 index 00000000000..99a8e4675ad --- /dev/null +++ b/src/main/java/hirehive/address/logic/commands/CommandUtil.java @@ -0,0 +1,78 @@ +package hirehive.address.logic.commands; + +import static hirehive.address.logic.Messages.MESSAGE_MULTIPLE_PEOPLE_QUERIED; +import static hirehive.address.logic.commands.EditCommand.createEditedPerson; +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import hirehive.address.commons.core.index.Index; +import hirehive.address.logic.Messages; +import hirehive.address.logic.commands.exceptions.CommandException; +import hirehive.address.logic.commands.queries.NameQuery; +import hirehive.address.logic.commands.queries.exceptions.QueryException; +import hirehive.address.model.Model; +import hirehive.address.model.person.Person; +import hirehive.address.model.person.PersonPredicate; + +/** + * Helper functions for handling command execution of similar types + */ +public class CommandUtil { + + /** + * Updates the displayed list based on the given {@code predicate} and returns a CommandResult indicating + * the results of the filter. + * @param model The model to update the list in + * @param predicate The given predicate to filter the list with + * @return A CommandResult object + */ + public static CommandResult executeFilter(Model model, PersonPredicate predicate) { + requireNonNull(model); + model.updateFilteredPersonList(predicate); + String message; + if (!model.getFilteredPersonList().isEmpty()) { + message = predicate.getSuccessString(); + } else { + message = Messages.MESSAGE_NO_SUCH_PERSON; + } + return new CommandResult(message); + } + + /** + * Uses the given query to find the matching person in the model. + * @param model The model to update the list in + * @param query The {@code NameQuery} object used to search for the person + * @return A {@code Person} object + * @throws CommandException + */ + public static Person querySearch(Model model, NameQuery query) throws CommandException { + requireNonNull(model); + List personToEdit; + try { + personToEdit = query.query(model); + } catch (QueryException qe) { + throw new CommandException(qe.getMessage()); + } + if (personToEdit.size() > 1) { + throw new CommandException(MESSAGE_MULTIPLE_PEOPLE_QUERIED); + } + return personToEdit.get(0); + } + /** + * Uses the given index to find the matching person in the model. + * @param model The model to update the list in + * @param index The {@code index} object used to search for the person + * @return A {@code Person} object + * @throws CommandException + */ + public static Person indexSearch(Model model, Index index) throws CommandException { + List lastShownList = model.getFilteredPersonList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + return lastShownList.get(index.getZeroBased()); + } +} diff --git a/src/main/java/hirehive/address/logic/commands/DeleteCommand.java b/src/main/java/hirehive/address/logic/commands/DeleteCommand.java new file mode 100644 index 00000000000..b0471847be1 --- /dev/null +++ b/src/main/java/hirehive/address/logic/commands/DeleteCommand.java @@ -0,0 +1,100 @@ +package hirehive.address.logic.commands; + +import static hirehive.address.logic.Messages.MESSAGE_DATA_SAVED; +import static hirehive.address.logic.Messages.MESSAGE_MULTIPLE_PEOPLE_QUERIED; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NAME; +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + +import java.util.List; +import java.util.Objects; + +import hirehive.address.commons.core.index.Index; +import hirehive.address.logic.Messages; +import hirehive.address.logic.commands.exceptions.CommandException; +import hirehive.address.logic.commands.queries.NameQuery; +import hirehive.address.logic.commands.queries.exceptions.QueryException; +import hirehive.address.model.Model; +import hirehive.address.model.person.Person; + + +/** + * Deletes a person identified using its 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 given name or index.\n" + + "Parameters (either 1 or 2): \n" + + " 1. " + PREFIX_NAME + "NAME\n" + + " 2. INDEX (must be a positive integer)\n" + + "Example:\n" + + " - " + COMMAND_WORD + " " + PREFIX_NAME + "John Doe\n" + + " - " + COMMAND_WORD + " " + "1"; + + public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; + + private NameQuery query = null; + + private Index index = null; + + /** + * Creates DeleteCommand to remove a person with specified name + * @param query the name of the person to be deleted + */ + public DeleteCommand(NameQuery query) { + requireNonNull(query); + this.query = query; + } + + /** + * Creates DeleteCommand to remove a person at the specified index + * @param index the specified index of the Person + */ + public DeleteCommand(Index index) { + requireNonNull(index); + this.index = index; + } + + /** + * Executes the delete command to remove a contact from the address book + * @param model {@code Model} which the command should operate on. + * @return A {@code CommandResult} containing success message + * @throws CommandException if provided index is out of bounds + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + Person personToDelete; + if (!isNull(query)) { + personToDelete = CommandUtil.querySearch(model, query); + } else if (!isNull(index)) { + personToDelete = CommandUtil.indexSearch(model, index); + } else { + throw new CommandException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, MESSAGE_USAGE)); + } + + model.deletePerson(personToDelete); + model.unfilterPersonList(); + return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete)), true); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof DeleteCommand)) { + return false; + } + + DeleteCommand otherDeleteCommand = (DeleteCommand) other; + return Objects.equals(this.query, otherDeleteCommand.query); + } + +} diff --git a/src/main/java/hirehive/address/logic/commands/DisplayNoteCommand.java b/src/main/java/hirehive/address/logic/commands/DisplayNoteCommand.java new file mode 100644 index 00000000000..5d67c89cb3c --- /dev/null +++ b/src/main/java/hirehive/address/logic/commands/DisplayNoteCommand.java @@ -0,0 +1,102 @@ +package hirehive.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; +import java.util.Objects; + +import hirehive.address.commons.util.ToStringBuilder; +import hirehive.address.logic.Messages; +import hirehive.address.logic.commands.exceptions.CommandException; +import hirehive.address.logic.commands.queries.NameQuery; +import hirehive.address.logic.commands.queries.exceptions.QueryException; +import hirehive.address.model.Model; +import hirehive.address.model.person.Name; +import hirehive.address.model.person.NameContainsKeywordsPredicate; +import hirehive.address.model.person.Person; + +/** + * Finds a Person by name and displays the note in a popup window. + */ +public class DisplayNoteCommand extends Command { + + public static final String COMMAND_WORD = "displaynote"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Displays the note of the person identified by the name used in the displayed person list.\n" + + "Parameters: n/NAME\nExample: " + COMMAND_WORD + " n/john doe"; + + public static final String MESSAGE_SUCCESS = "Displaying note of: %1$s"; + + private final String name; + + /** + * Creates DisplayNoteCommand to show the note of a person. + * @param name the name of the person. + */ + public DisplayNoteCommand(String name) { + requireNonNull(name); + this.name = name; + } + + /** + * Executes the displaynote command to display the note of the queried person + * @param model {@code Model} which the command should operate on. + * @return A {@code CommandResult} containing success message. + * @throws CommandException if person is not found. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + NameQuery query = new NameQuery(new NameContainsKeywordsPredicate(name)); + List personToDisplay; + try { + personToDisplay = query.query(model); + } catch (QueryException e) { + throw new CommandException(Messages.MESSAGE_NO_SUCH_PERSON); + } + + Person personDisplayed = null; + if (personToDisplay.size() > 1) { + Name givenName = new Name(name); + for (Person person : personToDisplay) { + if (person.getName().equals(givenName)) { + personDisplayed = person; + break; + } + } + if (personDisplayed == null) { + throw new CommandException(Messages.MESSAGE_MULTIPLE_PEOPLE_QUERIED_NAME); + } + } else { + personDisplayed = personToDisplay.get(0); + } + + model.updatePersonNote(personDisplayed); + return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(personDisplayed)), false, + false, true, false); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof DisplayNoteCommand)) { + return false; + } + + DisplayNoteCommand otherNoteCommand = (DisplayNoteCommand) other; + return name.equals(otherNoteCommand.name); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("name", name) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/hirehive/address/logic/commands/EditCommand.java similarity index 58% rename from src/main/java/seedu/address/logic/commands/EditCommand.java rename to src/main/java/hirehive/address/logic/commands/EditCommand.java index 4b581c7331e..a58666c80d7 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/hirehive/address/logic/commands/EditCommand.java @@ -1,12 +1,15 @@ -package seedu.address.logic.commands; - +package hirehive.address.logic.commands; + +import static hirehive.address.logic.Messages.MESSAGE_DATA_SAVED; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_DATE; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NAME; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NOTE; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_ROLE; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_TAG; 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; @@ -15,18 +18,21 @@ import java.util.Optional; import java.util.Set; -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.CollectionUtil; -import seedu.address.commons.util.ToStringBuilder; -import seedu.address.logic.Messages; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.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; +import hirehive.address.commons.core.index.Index; +import hirehive.address.commons.util.CollectionUtil; +import hirehive.address.commons.util.ToStringBuilder; +import hirehive.address.logic.Messages; +import hirehive.address.logic.commands.exceptions.CommandException; +import hirehive.address.model.Model; +import hirehive.address.model.person.Address; +import hirehive.address.model.person.Email; +import hirehive.address.model.person.InterviewDate; +import hirehive.address.model.person.Name; +import hirehive.address.model.person.Note; +import hirehive.address.model.person.Person; +import hirehive.address.model.person.Phone; +import hirehive.address.model.person.Role; +import hirehive.address.model.tag.Tag; /** * Edits the details of an existing person in the address book. @@ -43,7 +49,10 @@ public class EditCommand extends Command { + "[" + PREFIX_PHONE + "PHONE] " + "[" + PREFIX_EMAIL + "EMAIL] " + "[" + PREFIX_ADDRESS + "ADDRESS] " - + "[" + PREFIX_TAG + "TAG]...\n" + + "[" + PREFIX_ROLE + "ROLE] " + + "[" + PREFIX_TAG + "TAG] " + + "[" + PREFIX_NOTE + "NOTE]" + + "[" + PREFIX_DATE + "DATE]\n" + "Example: " + COMMAND_WORD + " 1 " + PREFIX_PHONE + "91234567 " + PREFIX_EMAIL + "johndoe@example.com"; @@ -84,24 +93,54 @@ public CommandResult execute(Model model) throws CommandException { } model.setPerson(personToEdit, editedPerson); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson))); + model.unfilterPersonList(); + model.updatePersonNote(editedPerson); + return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)), true); } /** * Creates and returns a {@code Person} with the details of {@code personToEdit} * edited with {@code editPersonDescriptor}. */ - private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { + protected 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()); + Role updatedRole = editPersonDescriptor.getRole().orElse(personToEdit.getRole()); + Tag updatedTag = editPersonDescriptor.getTag().orElse(personToEdit.getTag()); + Note updatedNote = editPersonDescriptor.getNote().orElse(personToEdit.getNote()); + InterviewDate updatedDate = editPersonDescriptor.getDate().orElse(personToEdit.getDate()); + + return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedRole, updatedTag, + updatedNote, updatedDate); + } + + protected static Person createOffsetTagPerson(Person personToEdit, int offset) { + assert personToEdit != null; + + Name name = personToEdit.getName(); + Phone phone = personToEdit.getPhone(); + Email email = personToEdit.getEmail(); + Address address = personToEdit.getAddress(); + Role role = personToEdit.getRole(); + Note note = personToEdit.getNote(); + InterviewDate date = personToEdit.getDate(); - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); + Tag updatedTag = personToEdit.getTag().offsetBy(offset); + + return new Person(name, phone, email, address, role, updatedTag, + note, date); + } + + protected Index getIndex() { + return index; + } + + protected EditPersonDescriptor getEditPersonDescriptor() { + return editPersonDescriptor; } @Override @@ -137,27 +176,33 @@ public static class EditPersonDescriptor { private Phone phone; private Email email; private Address address; - private Set tags; + private Role role; + private Tag tag; + private Note note; + private InterviewDate date; public EditPersonDescriptor() {} /** * Copy constructor. - * A defensive copy of {@code tags} is used internally. + * A defensive copy of {@code tag} is used internally. */ public EditPersonDescriptor(EditPersonDescriptor toCopy) { setName(toCopy.name); setPhone(toCopy.phone); setEmail(toCopy.email); setAddress(toCopy.address); - setTags(toCopy.tags); + setRole(toCopy.role); + setTag(toCopy.tag); + setNote(toCopy.note); + setDate(toCopy.date); } /** * Returns true if at least one field is edited. */ public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); + return CollectionUtil.isAnyNonNull(name, phone, email, address, role, tag, note, date); } public void setName(Name name) { @@ -192,21 +237,45 @@ public Optional
getAddress() { return Optional.ofNullable(address); } + public void setRole(Role role) { + this.role = role; + } + + public Optional getRole() { + return Optional.ofNullable(role); + } + + public void setNote(Note note) { + this.note = note; + } + + public Optional getNote() { + return Optional.ofNullable(note); + } + + public void setDate(InterviewDate date) { + this.date = date; + } + + public Optional getDate() { + return Optional.ofNullable(date); + } + /** - * Sets {@code tags} to this object's {@code tags}. - * A defensive copy of {@code tags} is used internally. + * Sets {@code tag} to this object's {@code tag}. + * A defensive copy of {@code tag} is used internally. */ - public void setTags(Set tags) { - this.tags = (tags != null) ? new HashSet<>(tags) : null; + public void setTag(Tag tag) { + this.tag = tag; } /** * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} * if modification is attempted. - * Returns {@code Optional#empty()} if {@code tags} is null. + * Returns {@code Optional#empty()} if {@code tag} is null. */ - public Optional> getTags() { - return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); + public Optional getTag() { + return Optional.ofNullable(tag); } @Override @@ -225,7 +294,10 @@ public boolean equals(Object other) { && Objects.equals(phone, otherEditPersonDescriptor.phone) && Objects.equals(email, otherEditPersonDescriptor.email) && Objects.equals(address, otherEditPersonDescriptor.address) - && Objects.equals(tags, otherEditPersonDescriptor.tags); + && Objects.equals(role, otherEditPersonDescriptor.role) + && Objects.equals(tag, otherEditPersonDescriptor.tag) + && Objects.equals(note, otherEditPersonDescriptor.note) + && Objects.equals(date, otherEditPersonDescriptor.date); } @Override @@ -235,7 +307,10 @@ public String toString() { .add("phone", phone) .add("email", email) .add("address", address) - .add("tags", tags) + .add("role", role) + .add("tag", tag) + .add("note", note) + .add("interviewDate", date) .toString(); } } diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/hirehive/address/logic/commands/ExitCommand.java similarity index 78% rename from src/main/java/seedu/address/logic/commands/ExitCommand.java rename to src/main/java/hirehive/address/logic/commands/ExitCommand.java index 3dd85a8ba90..03621a1ef96 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/hirehive/address/logic/commands/ExitCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package hirehive.address.logic.commands; -import seedu.address.model.Model; +import hirehive.address.model.Model; /** * Terminates the program. @@ -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, false); } } diff --git a/src/main/java/hirehive/address/logic/commands/FilterCommand.java b/src/main/java/hirehive/address/logic/commands/FilterCommand.java new file mode 100644 index 00000000000..8130f385332 --- /dev/null +++ b/src/main/java/hirehive/address/logic/commands/FilterCommand.java @@ -0,0 +1,54 @@ +package hirehive.address.logic.commands; + +import hirehive.address.commons.util.ToStringBuilder; +import hirehive.address.logic.commands.exceptions.CommandException; +import hirehive.address.logic.parser.CliSyntax; +import hirehive.address.model.Model; +import hirehive.address.model.person.PersonContainsTagPredicate; + +/** + * Finds and lists all persons in address book whose tags contain any of the argument tags. + * Tag matching is case-insensitive. + */ +public class FilterCommand extends Command { + public static final String COMMAND_WORD = "filter"; + public static final String NOT_IMPLEMENTED_TEXT = "Command not implemented yet"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Filters all persons with the given tag " + + "(case-insensitive) and displays them as a list with index numbers.\n" + + "Parameters: t/TAG\n" + + "Example: " + COMMAND_WORD + " " + CliSyntax.PREFIX_TAG + "applicant"; + + private final PersonContainsTagPredicate predicate; + + public FilterCommand(PersonContainsTagPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + return CommandUtil.executeFilter(model, predicate); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof FilterCommand)) { + return false; + } + + FilterCommand otherFilterCommand = (FilterCommand) other; + return predicate.equals(otherFilterCommand.predicate); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("predicate", predicate) + .toString(); + } +} diff --git a/src/main/java/hirehive/address/logic/commands/FilterOutCommand.java b/src/main/java/hirehive/address/logic/commands/FilterOutCommand.java new file mode 100644 index 00000000000..4130d76ee30 --- /dev/null +++ b/src/main/java/hirehive/address/logic/commands/FilterOutCommand.java @@ -0,0 +1,48 @@ +package hirehive.address.logic.commands; + +import hirehive.address.logic.parser.CliSyntax; +import hirehive.address.model.Model; +import hirehive.address.model.person.PersonDoesNotContainTagPredicate; + +/** + * Finds and lists all persons in address book whose tags do not + * contain any of the argument tags. + */ +public class FilterOutCommand extends Command { + public static final String COMMAND_WORD = "filterout"; + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Filters out all persons with the given tag (case-insensitive) " + + "and displays the remaining persons as a list.\n" + + "Parameters: t/TAG\n" + + "Example: " + COMMAND_WORD + " " + CliSyntax.PREFIX_TAG + "offered"; + + private final PersonDoesNotContainTagPredicate predicate; + + /** + * Creates FilterOutCommand to filter out applicants with the specified tag + * @param predicate the predicate to filter the list + */ + public FilterOutCommand(PersonDoesNotContainTagPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + return CommandUtil.executeFilter(model, predicate); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof FilterOutCommand)) { + return false; + } + + FilterOutCommand otherFilterOutCommand = (FilterOutCommand) other; + return predicate.equals(otherFilterOutCommand.predicate); + } +} diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/hirehive/address/logic/commands/FindCommand.java similarity index 73% rename from src/main/java/seedu/address/logic/commands/FindCommand.java rename to src/main/java/hirehive/address/logic/commands/FindCommand.java index 72b9eddd3a7..97b2bcb52b0 100644 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ b/src/main/java/hirehive/address/logic/commands/FindCommand.java @@ -1,15 +1,14 @@ -package seedu.address.logic.commands; +package hirehive.address.logic.commands; import static java.util.Objects.requireNonNull; -import seedu.address.commons.util.ToStringBuilder; -import seedu.address.logic.Messages; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import hirehive.address.commons.util.ToStringBuilder; +import hirehive.address.model.Model; +import hirehive.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. + * Keyword matching is case-insensitive. */ public class FindCommand extends Command { @@ -28,10 +27,7 @@ public FindCommand(NameContainsKeywordsPredicate 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())); + return CommandUtil.executeFilter(model, predicate); } @Override diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/hirehive/address/logic/commands/HelpCommand.java similarity index 72% rename from src/main/java/seedu/address/logic/commands/HelpCommand.java rename to src/main/java/hirehive/address/logic/commands/HelpCommand.java index bf824f91bd0..fd44708507c 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/hirehive/address/logic/commands/HelpCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package hirehive.address.logic.commands; -import seedu.address.model.Model; +import hirehive.address.model.Model; /** * Format full help instructions for every command for display. @@ -16,6 +16,7 @@ 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, false); } } diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/hirehive/address/logic/commands/ListCommand.java similarity index 66% rename from src/main/java/seedu/address/logic/commands/ListCommand.java rename to src/main/java/hirehive/address/logic/commands/ListCommand.java index 84be6ad2596..d7a364b4658 100644 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ b/src/main/java/hirehive/address/logic/commands/ListCommand.java @@ -1,9 +1,9 @@ -package seedu.address.logic.commands; +package hirehive.address.logic.commands; +import static hirehive.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; -import seedu.address.model.Model; +import hirehive.address.model.Model; /** * Lists all persons in the address book to the user. @@ -12,13 +12,13 @@ public class ListCommand extends Command { public static final String COMMAND_WORD = "list"; - public static final String MESSAGE_SUCCESS = "Listed all persons"; + public static final String MESSAGE_SUCCESS = "Listed all persons."; @Override public CommandResult execute(Model model) { requireNonNull(model); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + model.unfilterPersonList(); return new CommandResult(MESSAGE_SUCCESS); } } diff --git a/src/main/java/hirehive/address/logic/commands/NewNoteCommand.java b/src/main/java/hirehive/address/logic/commands/NewNoteCommand.java new file mode 100644 index 00000000000..1cf31e5174d --- /dev/null +++ b/src/main/java/hirehive/address/logic/commands/NewNoteCommand.java @@ -0,0 +1,112 @@ +package hirehive.address.logic.commands; + +import static hirehive.address.logic.Messages.MESSAGE_DATA_SAVED; +import static hirehive.address.logic.commands.EditCommand.createEditedPerson; +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import hirehive.address.commons.util.ToStringBuilder; +import hirehive.address.logic.Messages; +import hirehive.address.logic.commands.EditCommand.EditPersonDescriptor; +import hirehive.address.logic.commands.exceptions.CommandException; +import hirehive.address.logic.commands.queries.NameQuery; +import hirehive.address.logic.commands.queries.exceptions.QueryException; +import hirehive.address.model.Model; +import hirehive.address.model.person.Name; +import hirehive.address.model.person.NameContainsKeywordsPredicate; +import hirehive.address.model.person.Person; + +/** + * Finds a Person by name, then creates and displays the note in a pop up window. + */ +public class NewNoteCommand extends Command { + + public static final String COMMAND_WORD = "newnote"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Creates a note for the person identified by the name used in the displayed person list. \n" + + "Parameters: n/NAME i/NOTE\nExample: " + COMMAND_WORD + " n/john doe i/Good at expressing himself"; + + public static final String MESSAGE_SUCCESS = "Added and displaying note of: %1$s"; + + private final String name; + private final EditPersonDescriptor editPersonDescriptor; + + /** + * Creates NewnoteCommand to create and show the note of a person. + * @param name the name of the person. + */ + public NewNoteCommand(String name, EditPersonDescriptor editPersonDescriptor) { + requireNonNull(name); + requireNonNull(editPersonDescriptor); + + this.name = name; + this.editPersonDescriptor = editPersonDescriptor; + } + + /** + * Executes the newnote command to add and display the note of the queried person + * @param model {@code Model} which the command should operate on. + * @return A {@code CommandResult} containing success message. + * @throws CommandException if person is not found. + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + NameQuery query = new NameQuery(new NameContainsKeywordsPredicate(name)); + List personToAddNote; + try { + personToAddNote = query.query(model); + } catch (QueryException e) { + throw new CommandException(Messages.MESSAGE_NO_SUCH_PERSON); + } + + Person personAddedNote = null; + if (personToAddNote.size() > 1) { + Name givenName = new Name(name); + for (Person person : personToAddNote) { + if (person.getName().equals(givenName)) { + personAddedNote = person; + break; + } + } + if (personAddedNote == null) { + throw new CommandException(Messages.MESSAGE_MULTIPLE_PEOPLE_QUERIED_NAME); + } + } else { + personAddedNote = personToAddNote.get(0); + } + + Person editedPerson = createEditedPerson(personAddedNote, editPersonDescriptor); + + model.setPerson(personAddedNote, editedPerson); + model.updatePersonNote(editedPerson); + return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(editedPerson)), + false, false, true, true); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof NewNoteCommand)) { + return false; + } + + NewNoteCommand otherNewNoteCommand = (NewNoteCommand) other; + return name.equals(otherNewNoteCommand.name) + && editPersonDescriptor.equals(otherNewNoteCommand.editPersonDescriptor); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("name", name) + .add("editPersonDescriptor", editPersonDescriptor) + .toString(); + } +} diff --git a/src/main/java/hirehive/address/logic/commands/ReminderCommand.java b/src/main/java/hirehive/address/logic/commands/ReminderCommand.java new file mode 100644 index 00000000000..d7700e69676 --- /dev/null +++ b/src/main/java/hirehive/address/logic/commands/ReminderCommand.java @@ -0,0 +1,52 @@ +package hirehive.address.logic.commands; + +import hirehive.address.commons.util.ToStringBuilder; +import hirehive.address.logic.commands.exceptions.CommandException; +import hirehive.address.model.Model; +import hirehive.address.model.person.UpcomingInterviewPredicate; + +/** + * Finds and lists all persons in address book whose interviews are coming up within a number of days. + * Filter is start and end date-inclusive. + */ +public class ReminderCommand extends Command { + public static final String COMMAND_WORD = "remind"; + public static final String NOT_IMPLEMENTED_TEXT = "Command not implemented yet"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Filters all persons with an upcoming interview " + + "within the given amount of days and displays them as a list with index numbers.\n" + + "Parameters: DAYS (must be a non-negative integer)\n" + + "Example: " + COMMAND_WORD + " 3"; + + private final UpcomingInterviewPredicate predicate; + + public ReminderCommand(UpcomingInterviewPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + return CommandUtil.executeFilter(model, predicate); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ReminderCommand)) { + return false; + } + + ReminderCommand otherReminderCommand = (ReminderCommand) other; + return predicate.equals(otherReminderCommand.predicate); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("predicate", predicate) + .toString(); + } +} diff --git a/src/main/java/hirehive/address/logic/commands/ScheduleCommand.java b/src/main/java/hirehive/address/logic/commands/ScheduleCommand.java new file mode 100644 index 00000000000..332060a993b --- /dev/null +++ b/src/main/java/hirehive/address/logic/commands/ScheduleCommand.java @@ -0,0 +1,142 @@ +package hirehive.address.logic.commands; + +import static hirehive.address.logic.commands.EditCommand.createEditedPerson; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_DATE; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NAME; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_TAG; +import static java.util.Objects.requireNonNull; + +import hirehive.address.commons.core.index.Index; +import hirehive.address.logic.Messages; +import hirehive.address.logic.commands.exceptions.CommandException; +import hirehive.address.logic.commands.queries.NameQuery; +import hirehive.address.model.Model; +import hirehive.address.model.person.NameContainsKeywordsPredicate; +import hirehive.address.model.person.Person; +import hirehive.address.model.tag.Tag; + +/** + * Adds or edits the interview date of a Person + */ +public class ScheduleCommand extends Command { + public static final String COMMAND_WORD = "schedule"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Adds or edits the interview date for the person identified by the name used in\n" + + "the displayed person list.\n" + + "If no date is specified, the next available date starting from the next day will be used instead.\n" + + "Parameters (either 1 or 2):\n" + + " 1. " + PREFIX_NAME + "NAME [" + PREFIX_DATE + "DATE]\n" + + " 2. INDEX [" + PREFIX_DATE + "NAME]\n" + + "Example:\n - " + COMMAND_WORD + " " + PREFIX_NAME + "John " + PREFIX_DATE + "01/05/2025\n" + + " - " + COMMAND_WORD + " 1 " + PREFIX_DATE + "01/05/2025"; + + public static final String MESSAGE_DATE_PERSON_SUCCESS = "Added interview date: %1$s"; + public static final String MESSAGE_INVALID_PERSON = "%1$s has invalid tag: %2$s.\n" + + "You can only schedule interviews with people who are Applicants, Candidates or Interviewees."; + public static final String MESSAGE_NOT_IMPLEMENTED_YET = + "Date command not implemented yet"; + + private final NameQuery query; + private final EditCommand.EditPersonDescriptor editPersonDescriptor; + private final Index index; + + private final boolean bDateProvided; + + /** + * Creates a {@code ScheduleCommand} object with a provided date. + */ + public ScheduleCommand(NameQuery query, EditCommand.EditPersonDescriptor editPersonDescriptor) { + requireNonNull(query); + requireNonNull(editPersonDescriptor); + this.query = query; + this.editPersonDescriptor = editPersonDescriptor; + this.index = null; + bDateProvided = true; + } + + /** + * Creates a {@code ScheduleCommand} object without a provided date. + */ + public ScheduleCommand(NameQuery query) { + requireNonNull(query); + this.query = query; + this.editPersonDescriptor = new EditCommand.EditPersonDescriptor(); + this.index = null; + bDateProvided = false; + } + + /** + * Creates a {@code ScheduleCommand} object with a provided date. + */ + public ScheduleCommand(Index index, EditCommand.EditPersonDescriptor editPersonDescriptor) { + requireNonNull(index); + requireNonNull(editPersonDescriptor); + this.index = index; + this.query = null; + this.editPersonDescriptor = editPersonDescriptor; + bDateProvided = true; + } + + /** + * Creates a {@code ScheduleCommand} object without a provided date. + */ + public ScheduleCommand(Index index) { + requireNonNull(index); + this.index = index; + this.query = null; + this.editPersonDescriptor = new EditCommand.EditPersonDescriptor(); + bDateProvided = false; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + if (!bDateProvided) { + editPersonDescriptor.setDate(model.getAvailableDate()); + } + + Person personToAddDate; + if (index == null) { + personToAddDate = CommandUtil.querySearch(model, query); + } else { + personToAddDate = CommandUtil.indexSearch(model, index); + } + + if (personToAddDate.getTag().equals(Tag.APPLICANT) || personToAddDate.getTag().equals(Tag.CANDIDATE)) { + editPersonDescriptor.setTag(Tag.INTERVIEWEE); + } else if (personToAddDate.getTag().equals(Tag.OFFERED) || personToAddDate.getTag().equals(Tag.REJECTED)) { + throw new CommandException(String.format(MESSAGE_INVALID_PERSON, personToAddDate.getName().toString(), + personToAddDate.getTag().getTagName())); + } + Person editedPerson = createEditedPerson(personToAddDate, editPersonDescriptor); + model.setPerson(personToAddDate, editedPerson); + model.unfilterPersonList(); + model.updateFilteredPersonList(new NameContainsKeywordsPredicate(editedPerson.getName().fullName)); + return new CommandResult(String.format(MESSAGE_DATE_PERSON_SUCCESS, Messages.format(editedPerson)), true); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ScheduleCommand)) { + return false; + } + + ScheduleCommand otherCommand = (ScheduleCommand) other; + if (!this.editPersonDescriptor.equals(otherCommand.editPersonDescriptor)) { + return false; + } + if (this.query == null) { + return this.index.equals(otherCommand.index) && otherCommand.query == null; + } + if (this.index == null) { + return this.query.equals(otherCommand.query) && otherCommand.index == null; + } + return false; + } +} diff --git a/src/main/java/hirehive/address/logic/commands/SortCommand.java b/src/main/java/hirehive/address/logic/commands/SortCommand.java new file mode 100644 index 00000000000..afebdca285f --- /dev/null +++ b/src/main/java/hirehive/address/logic/commands/SortCommand.java @@ -0,0 +1,22 @@ +package hirehive.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import hirehive.address.model.Model; + +/** + * Sorts the applicants by their interview date + */ +public class SortCommand extends Command { + public static final String COMMAND_WORD = "sort"; + public static final String MESSAGE_SUCCESS = "Applicants have been sorted by their interview date!"; + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.sortPersons(); + return new CommandResult(MESSAGE_SUCCESS); + } + + +} diff --git a/src/main/java/hirehive/address/logic/commands/TagCommand.java b/src/main/java/hirehive/address/logic/commands/TagCommand.java new file mode 100644 index 00000000000..8c655218be2 --- /dev/null +++ b/src/main/java/hirehive/address/logic/commands/TagCommand.java @@ -0,0 +1,131 @@ +package hirehive.address.logic.commands; + +import static hirehive.address.logic.Messages.MESSAGE_MULTIPLE_PEOPLE_QUERIED; +import static hirehive.address.logic.commands.EditCommand.createEditedPerson; +import static hirehive.address.logic.commands.EditCommand.createOffsetTagPerson; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NAME; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_TAG; +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import hirehive.address.commons.core.index.Index; +import hirehive.address.logic.Messages; +import hirehive.address.logic.commands.EditCommand.EditPersonDescriptor; +import hirehive.address.logic.commands.exceptions.CommandException; +import hirehive.address.logic.commands.queries.NameQuery; +import hirehive.address.logic.commands.queries.exceptions.QueryException; +import hirehive.address.model.Model; +import hirehive.address.model.person.Person; + +/** + * Tags a person identified using their name or by index. + */ +public class TagCommand extends Command { + + public static final String COMMAND_WORD = "tag"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Tags the person identified by the given name or index.\n" + + "Parameters (either 1, 2, 3):\n" + + " 1. " + PREFIX_NAME + "NAME " + PREFIX_TAG + "TAG\n" + + " 2. OFFSET " + PREFIX_NAME + "NAME\n" + + " 3. INDEX (must be a positive integer) " + PREFIX_TAG + "TAG\n" + + "Example:\n" + + " - " + COMMAND_WORD + " " + PREFIX_NAME + "John " + PREFIX_TAG + "Applicant\n" + + " - " + COMMAND_WORD + " +1 " + PREFIX_NAME + "John\n" + + " - " + COMMAND_WORD + " 1 " + PREFIX_TAG + "Applicant\n"; + + public static final String MESSAGE_TAG_INVALID_PARAMS = "The given Tag parameters are invalid."; + public static final String MESSAGE_TAG_INVALID_OFFSET = "The given offset is invalid. It should range from -4 to " + + "+4."; + public static final String MESSAGE_TAG_PERSON_SUCCESS = "Tagged Person: %1$s"; + + private NameQuery query = null; + private EditPersonDescriptor editPersonDescriptor = null; + private Index index = null; + private int offset = 0; + + /** + * @param query to query for the specific person by name + * @param editPersonDescriptor details to tag the person with + */ + public TagCommand(NameQuery query, EditCommand.EditPersonDescriptor editPersonDescriptor) { + requireNonNull(query); + requireNonNull(editPersonDescriptor); + + this.query = query; + this.editPersonDescriptor = editPersonDescriptor; + } + + /** + * @param query to query for the specific person by name + * @param offset of the hiring stage tag from the person's tag + */ + public TagCommand(NameQuery query, int offset) { + requireNonNull(query); + + this.query = query; + this.offset = offset; + } + + /** + * @param index of the person in the filtered person list to tag + * @param editPersonDescriptor details to tag the person with + */ + public TagCommand(Index index, EditCommand.EditPersonDescriptor editPersonDescriptor) { + requireNonNull(index); + requireNonNull(editPersonDescriptor); + + this.index = index; + this.editPersonDescriptor = editPersonDescriptor; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + Person personToTag; + + if (isNull(index)) { + if (isNull(editPersonDescriptor)) { + personToTag = CommandUtil.querySearch(model, query); + Person taggedPerson = createOffsetTagPerson(personToTag, offset); + + model.setPerson(personToTag, taggedPerson); + return new CommandResult(String.format(MESSAGE_TAG_PERSON_SUCCESS, Messages.format(taggedPerson))); + } + + personToTag = CommandUtil.querySearch(model, query); + Person taggedPerson = createEditedPerson(personToTag, editPersonDescriptor); + + model.setPerson(personToTag, taggedPerson); + return new CommandResult(String.format(MESSAGE_TAG_PERSON_SUCCESS, Messages.format(taggedPerson))); + } + if (isNull(query)) { + personToTag = CommandUtil.indexSearch(model, index); + Person taggedPerson = createEditedPerson(personToTag, editPersonDescriptor); + + model.setPerson(personToTag, taggedPerson); + return new CommandResult(String.format(MESSAGE_TAG_PERSON_SUCCESS, Messages.format(taggedPerson))); + } + + throw new CommandException(MESSAGE_TAG_INVALID_PARAMS); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof TagCommand)) { + return false; + } + + TagCommand otherTagCommand = (TagCommand) other; + return query.equals(otherTagCommand.query) + && editPersonDescriptor.equals(otherTagCommand.editPersonDescriptor); + } +} diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/hirehive/address/logic/commands/exceptions/CommandException.java similarity index 89% rename from src/main/java/seedu/address/logic/commands/exceptions/CommandException.java rename to src/main/java/hirehive/address/logic/commands/exceptions/CommandException.java index a16bd14f2cd..f3d342007b8 100644 --- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java +++ b/src/main/java/hirehive/address/logic/commands/exceptions/CommandException.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands.exceptions; +package hirehive.address.logic.commands.exceptions; /** * Represents an error which occurs during execution of a {@link Command}. diff --git a/src/main/java/hirehive/address/logic/commands/queries/NameQuery.java b/src/main/java/hirehive/address/logic/commands/queries/NameQuery.java new file mode 100644 index 00000000000..95d645ba915 --- /dev/null +++ b/src/main/java/hirehive/address/logic/commands/queries/NameQuery.java @@ -0,0 +1,46 @@ +package hirehive.address.logic.commands.queries; + +import static hirehive.address.logic.Messages.MESSAGE_NO_SUCH_PERSON; + +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; + +import hirehive.address.logic.commands.queries.exceptions.QueryException; +import hirehive.address.model.Model; +import hirehive.address.model.person.Person; + +/** + * Queries for a person by a given name. + */ +public class NameQuery extends Query { + /** + * @param predicate to query a model with to get a person + */ + public NameQuery(Predicate predicate) { + super(predicate); + } + + @Override + public List query(Model model) throws QueryException { + model.unfilterPersonList(); + model.updateFilteredPersonList(this.predicate); + List filteredList = model.getFilteredPersonList(); + if (filteredList.isEmpty()) { + throw new QueryException(MESSAGE_NO_SUCH_PERSON); + } + return filteredList; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + NameQuery nameQuery = (NameQuery) other; + return Objects.equals(this.predicate, nameQuery.predicate); + } +} diff --git a/src/main/java/hirehive/address/logic/commands/queries/Query.java b/src/main/java/hirehive/address/logic/commands/queries/Query.java new file mode 100644 index 00000000000..e75182631c6 --- /dev/null +++ b/src/main/java/hirehive/address/logic/commands/queries/Query.java @@ -0,0 +1,30 @@ +package hirehive.address.logic.commands.queries; + +import java.util.List; +import java.util.function.Predicate; + +import hirehive.address.logic.commands.queries.exceptions.QueryException; +import hirehive.address.model.Model; + +/** + * Represents a query with hidden predicate to query with + */ +public abstract class Query { + protected final Predicate predicate; + + /** + * @param predicate to query a model with to get a result + */ + public Query(Predicate predicate) { + this.predicate = predicate; + } + + /** + * Queries the model with specified predicate and returns a specific object instance based + * + * @param model {@code Model} which the query should operate on. + * @return queried result from using the predicate + * @throws QueryException If an error occurs during query. + */ + public abstract List query(Model model) throws QueryException; +} diff --git a/src/main/java/hirehive/address/logic/commands/queries/exceptions/QueryException.java b/src/main/java/hirehive/address/logic/commands/queries/exceptions/QueryException.java new file mode 100644 index 00000000000..08eff964c9e --- /dev/null +++ b/src/main/java/hirehive/address/logic/commands/queries/exceptions/QueryException.java @@ -0,0 +1,14 @@ +package hirehive.address.logic.commands.queries.exceptions; + +/** + * Represents an error encountered by from a query. + */ +public class QueryException extends Exception { + public QueryException(String message) { + super(message); + } + + public QueryException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/hirehive/address/logic/parser/AddCommandParser.java similarity index 52% rename from src/main/java/seedu/address/logic/parser/AddCommandParser.java rename to src/main/java/hirehive/address/logic/parser/AddCommandParser.java index 4ff1a97ed77..13855989f8d 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/hirehive/address/logic/parser/AddCommandParser.java @@ -1,23 +1,25 @@ -package seedu.address.logic.parser; +package hirehive.address.logic.parser; -import static seedu.address.logic.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 static hirehive.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NAME; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NOTE; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_ROLE; -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; +import hirehive.address.logic.commands.AddCommand; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.person.Address; +import hirehive.address.model.person.Email; +import hirehive.address.model.person.Name; +import hirehive.address.model.person.Note; +import hirehive.address.model.person.Person; +import hirehive.address.model.person.Phone; +import hirehive.address.model.person.Role; +import hirehive.address.model.tag.Tag; /** * Parses input arguments and creates a new AddCommand object @@ -31,21 +33,22 @@ public class AddCommandParser implements Parser { */ public AddCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArgumentTokenizer.tokenize( + args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_ROLE); - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ROLE) || !argMultimap.getPreamble().isEmpty()) { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); } - argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS); + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_ROLE); 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)); + Role role = ParserUtil.parseRole(argMultimap.getValue(PREFIX_ROLE).get()); - Person person = new Person(name, phone, email, address, tagList); + Person person = Person.createDefaultPerson(name, phone, email, address, role); return new AddCommand(person); } @@ -57,5 +60,4 @@ public AddCommand parse(String args) throws ParseException { 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/hirehive/address/logic/parser/AddressBookParser.java similarity index 54% rename from src/main/java/seedu/address/logic/parser/AddressBookParser.java rename to src/main/java/hirehive/address/logic/parser/AddressBookParser.java index 3149ee07e0b..1fa7ea341ed 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/hirehive/address/logic/parser/AddressBookParser.java @@ -1,23 +1,32 @@ -package seedu.address.logic.parser; +package hirehive.address.logic.parser; -import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND; +import static hirehive.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static hirehive.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; -import seedu.address.commons.core.LogsCenter; -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; +import hirehive.address.commons.core.LogsCenter; +import hirehive.address.logic.commands.AddCommand; +import hirehive.address.logic.commands.ClearCommand; +import hirehive.address.logic.commands.Command; +import hirehive.address.logic.commands.DeleteCommand; +import hirehive.address.logic.commands.DisplayNoteCommand; +import hirehive.address.logic.commands.EditCommand; +import hirehive.address.logic.commands.ExitCommand; +import hirehive.address.logic.commands.FilterCommand; +import hirehive.address.logic.commands.FilterOutCommand; +import hirehive.address.logic.commands.FindCommand; +import hirehive.address.logic.commands.HelpCommand; +import hirehive.address.logic.commands.ListCommand; +import hirehive.address.logic.commands.NewNoteCommand; +import hirehive.address.logic.commands.ReminderCommand; +import hirehive.address.logic.commands.ScheduleCommand; +import hirehive.address.logic.commands.SortCommand; +import hirehive.address.logic.commands.TagCommand; +import hirehive.address.logic.parser.exceptions.ParseException; + /** * Parses user input. @@ -62,6 +71,9 @@ public Command parseCommand(String userInput) throws ParseException { case DeleteCommand.COMMAND_WORD: return new DeleteCommandParser().parse(arguments); + case TagCommand.COMMAND_WORD: + return new TagCommandParser().parse(arguments); + case ClearCommand.COMMAND_WORD: return new ClearCommand(); @@ -77,6 +89,26 @@ public Command parseCommand(String userInput) throws ParseException { case HelpCommand.COMMAND_WORD: return new HelpCommand(); + case ScheduleCommand.COMMAND_WORD: + return new ScheduleCommandParser().parse(arguments); + + case FilterCommand.COMMAND_WORD: + return new FilterCommandParser().parse(arguments); + + case ReminderCommand.COMMAND_WORD: + return new ReminderCommandParser().parse(arguments); + + case SortCommand.COMMAND_WORD: + return new SortCommand(); + + case DisplayNoteCommand.COMMAND_WORD: + return new DisplayNoteCommandParser().parse(arguments); + + case NewNoteCommand.COMMAND_WORD: + return new NewNoteCommandParser().parse(arguments); + + case FilterOutCommand.COMMAND_WORD: + return new FilterOutCommandParser().parse(arguments); default: logger.finer("This user input caused a ParseException: " + userInput); throw new ParseException(MESSAGE_UNKNOWN_COMMAND); diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/hirehive/address/logic/parser/ArgumentMultimap.java similarity index 95% rename from src/main/java/seedu/address/logic/parser/ArgumentMultimap.java rename to src/main/java/hirehive/address/logic/parser/ArgumentMultimap.java index 21e26887a83..11682b7357e 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ b/src/main/java/hirehive/address/logic/parser/ArgumentMultimap.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package hirehive.address.logic.parser; import java.util.ArrayList; import java.util.HashMap; @@ -7,8 +7,8 @@ import java.util.Optional; import java.util.stream.Stream; -import seedu.address.logic.Messages; -import seedu.address.logic.parser.exceptions.ParseException; +import hirehive.address.logic.Messages; +import hirehive.address.logic.parser.exceptions.ParseException; /** * Stores mapping of prefixes to their respective arguments. diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/hirehive/address/logic/parser/ArgumentTokenizer.java similarity index 99% rename from src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java rename to src/main/java/hirehive/address/logic/parser/ArgumentTokenizer.java index 5c9aebfa488..399dc15c75e 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ b/src/main/java/hirehive/address/logic/parser/ArgumentTokenizer.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package hirehive.address.logic.parser; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/hirehive/address/logic/parser/CliSyntax.java similarity index 67% rename from src/main/java/seedu/address/logic/parser/CliSyntax.java rename to src/main/java/hirehive/address/logic/parser/CliSyntax.java index 75b1a9bf119..e45cebdee2f 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/hirehive/address/logic/parser/CliSyntax.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package hirehive.address.logic.parser; /** * Contains Command Line Interface (CLI) syntax definitions common to multiple commands @@ -10,6 +10,8 @@ public class CliSyntax { 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_ROLE = new Prefix("r/"); public static final Prefix PREFIX_TAG = new Prefix("t/"); - + public static final Prefix PREFIX_DATE = new Prefix("id/"); + public static final Prefix PREFIX_NOTE = new Prefix("i/"); } diff --git a/src/main/java/hirehive/address/logic/parser/DeleteCommandParser.java b/src/main/java/hirehive/address/logic/parser/DeleteCommandParser.java new file mode 100644 index 00000000000..16e5381e0e5 --- /dev/null +++ b/src/main/java/hirehive/address/logic/parser/DeleteCommandParser.java @@ -0,0 +1,52 @@ +package hirehive.address.logic.parser; + +import static hirehive.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NAME; +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; + +import hirehive.address.commons.core.index.Index; +import hirehive.address.logic.commands.DeleteCommand; +import hirehive.address.logic.commands.queries.NameQuery; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.person.NameContainsKeywordsPredicate; + +/** + * 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 { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME); + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME); + + boolean hasIndex = !argMultimap.getPreamble().isBlank(); + boolean hasName = argMultimap.getValue(PREFIX_NAME).isPresent(); + + if (hasIndex && hasName) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); + } + + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + String nameKeywords = argMultimap.getValue(PREFIX_NAME).get().trim(); + if (nameKeywords.isBlank()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); + } + NameQuery nameQuery = new NameQuery(new NameContainsKeywordsPredicate(nameKeywords)); + return new DeleteCommand(nameQuery); + } + + try { + Index index = ParserUtil.parseIndex(argMultimap.getPreamble()); + return new DeleteCommand(index); + } catch (ParseException e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); + } + } +} diff --git a/src/main/java/hirehive/address/logic/parser/DisplayNoteCommandParser.java b/src/main/java/hirehive/address/logic/parser/DisplayNoteCommandParser.java new file mode 100644 index 00000000000..4fdc96908b4 --- /dev/null +++ b/src/main/java/hirehive/address/logic/parser/DisplayNoteCommandParser.java @@ -0,0 +1,38 @@ +package hirehive.address.logic.parser; + +import static hirehive.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NAME; +import static java.util.Objects.requireNonNull; + +import hirehive.address.logic.commands.DisplayNoteCommand; +import hirehive.address.logic.commands.queries.NameQuery; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.person.NameContainsKeywordsPredicate; + +/** + * Parses input argument and creates a new DisplayNoteCommand object. + */ +public class DisplayNoteCommandParser implements Parser { + + /** + * Parses the given {@code String} argument in the context of the DisplayNoteCommand + * and returns a DisplayNoteCommand object for execution. + * @throws ParseException if the user input does not conform the expected format. + */ + public DisplayNoteCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME); + + if (argMultimap.getValue(PREFIX_NAME).orElse("").trim().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DisplayNoteCommand.MESSAGE_USAGE)); + } + + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME); + + String nameKeyword = argMultimap.getValue(PREFIX_NAME).get(); + //NameQuery nameQuery = new NameQuery(new NameContainsKeywordsPredicate(nameKeyword)); + + return new DisplayNoteCommand(nameKeyword); + } + +} diff --git a/src/main/java/hirehive/address/logic/parser/EditCommandParser.java b/src/main/java/hirehive/address/logic/parser/EditCommandParser.java new file mode 100644 index 00000000000..040a591ddcb --- /dev/null +++ b/src/main/java/hirehive/address/logic/parser/EditCommandParser.java @@ -0,0 +1,86 @@ +package hirehive.address.logic.parser; + +import static hirehive.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_DATE; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NAME; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NOTE; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_ROLE; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_TAG; +import static java.util.Objects.requireNonNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import hirehive.address.commons.core.index.Index; +import hirehive.address.logic.commands.EditCommand; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.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_ROLE, PREFIX_TAG, + PREFIX_NOTE, PREFIX_DATE); + + Index index; + + if (argMultimap.getPreamble().trim().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE)); + } else { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } + + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, + PREFIX_TAG, PREFIX_NOTE, + PREFIX_DATE); + + EditCommand.EditPersonDescriptor editPersonDescriptor = new EditCommand.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())); + } + if (argMultimap.getValue(PREFIX_ROLE).isPresent()) { + editPersonDescriptor.setRole(ParserUtil.parseRole(argMultimap.getValue(PREFIX_ROLE).get())); + } + if (argMultimap.getValue(PREFIX_TAG).isPresent()) { + editPersonDescriptor.setTag(ParserUtil.parseTag(argMultimap.getValue(PREFIX_TAG).get())); + } + if (argMultimap.getValue(PREFIX_NOTE).isPresent()) { + editPersonDescriptor.setNote(ParserUtil.parseNote(argMultimap.getValue(PREFIX_NOTE).get())); + } + if (argMultimap.getValue(PREFIX_DATE).isPresent()) { + editPersonDescriptor.setDate(ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get())); + } + if (!editPersonDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); + } + + return new EditCommand(index, editPersonDescriptor); + } +} diff --git a/src/main/java/hirehive/address/logic/parser/FilterCommandParser.java b/src/main/java/hirehive/address/logic/parser/FilterCommandParser.java new file mode 100644 index 00000000000..e0d4fddd634 --- /dev/null +++ b/src/main/java/hirehive/address/logic/parser/FilterCommandParser.java @@ -0,0 +1,34 @@ +package hirehive.address.logic.parser; + +import static hirehive.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_TAG; +import static java.util.Objects.requireNonNull; + +import hirehive.address.logic.commands.FilterCommand; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.person.PersonContainsTagPredicate; +import hirehive.address.model.tag.Tag; + +/** + * Parses input arguments and creates a new FilterCommand object + */ +public class FilterCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FilterCommand + * and returns a FilterCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FilterCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TAG); + Tag tag; + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_TAG); + if (argMultimap.getValue(PREFIX_TAG).isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_USAGE)); + } + tag = ParserUtil.parseTag(argMultimap.getValue(PREFIX_TAG).get()); + return new FilterCommand(new PersonContainsTagPredicate(tag)); + } +} diff --git a/src/main/java/hirehive/address/logic/parser/FilterOutCommandParser.java b/src/main/java/hirehive/address/logic/parser/FilterOutCommandParser.java new file mode 100644 index 00000000000..1fb5e6d8649 --- /dev/null +++ b/src/main/java/hirehive/address/logic/parser/FilterOutCommandParser.java @@ -0,0 +1,39 @@ +package hirehive.address.logic.parser; + +import static hirehive.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NAME; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_TAG; +import static java.util.Objects.requireNonNull; + +import hirehive.address.logic.commands.FilterOutCommand; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.person.PersonDoesNotContainTagPredicate; +import hirehive.address.model.tag.Tag; + +/** + * Parses input arguments and creates a new FilterOutCommand object + */ +public class FilterOutCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the FilterOutCommand + * and returns a FilterOutCommand object for execution. + * @param args the user input + * @return a new FilterOutCommand + * @throws ParseException if the user input does not conform the expected format + */ + public FilterOutCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TAG); + + Tag tag; + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_TAG); + if (argMultimap.getValue(PREFIX_NAME).map(String::trim).filter(String::isEmpty).isPresent()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterOutCommand.MESSAGE_USAGE)); + } + + tag = ParserUtil.parseTag(argMultimap.getValue(PREFIX_TAG).orElseThrow(() -> + new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterOutCommand.MESSAGE_USAGE)))); + return new FilterOutCommand(new PersonDoesNotContainTagPredicate(tag)); + } +} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/hirehive/address/logic/parser/FindCommandParser.java similarity index 56% rename from src/main/java/seedu/address/logic/parser/FindCommandParser.java rename to src/main/java/hirehive/address/logic/parser/FindCommandParser.java index 2867bde857b..cf36208939b 100644 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ b/src/main/java/hirehive/address/logic/parser/FindCommandParser.java @@ -1,12 +1,9 @@ -package seedu.address.logic.parser; +package hirehive.address.logic.parser; -import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; - -import java.util.Arrays; - -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import hirehive.address.logic.Messages; +import hirehive.address.logic.commands.FindCommand; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.person.NameContainsKeywordsPredicate; /** * Parses input arguments and creates a new FindCommand object @@ -22,12 +19,10 @@ 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.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); } - String[] nameKeywords = trimmedArgs.split("\\s+"); - - return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + return new FindCommand(new NameContainsKeywordsPredicate(trimmedArgs)); } } diff --git a/src/main/java/hirehive/address/logic/parser/NewNoteCommandParser.java b/src/main/java/hirehive/address/logic/parser/NewNoteCommandParser.java new file mode 100644 index 00000000000..5a24e6da000 --- /dev/null +++ b/src/main/java/hirehive/address/logic/parser/NewNoteCommandParser.java @@ -0,0 +1,43 @@ +package hirehive.address.logic.parser; + +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NAME; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NOTE; +import static java.util.Objects.requireNonNull; + +import hirehive.address.logic.Messages; +import hirehive.address.logic.commands.EditCommand.EditPersonDescriptor; +import hirehive.address.logic.commands.NewNoteCommand; +import hirehive.address.logic.commands.queries.NameQuery; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.person.NameContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new NewNoteCommand object + */ +public class NewNoteCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the NewNoteCommand + * and returns a NewNoteCommand object for execution. + * @throws ParseException if the user input does not conform to the expected format + */ + public NewNoteCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_NOTE); + if (argMultimap.getValue(PREFIX_NAME).orElse("").trim().isEmpty() + || argMultimap.getValue(PREFIX_NOTE).isEmpty()) { + throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, + NewNoteCommand.MESSAGE_USAGE)); + } + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_NOTE); + + String name = argMultimap.getValue(PREFIX_NAME).get(); + //NameQuery nameQuery = new NameQuery(new NameContainsKeywordsPredicate(name)); + + EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); + editPersonDescriptor.setNote(ParserUtil.parseNote(argMultimap.getValue(PREFIX_NOTE).get().trim())); + + return new NewNoteCommand(name, editPersonDescriptor); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/hirehive/address/logic/parser/Parser.java similarity index 71% rename from src/main/java/seedu/address/logic/parser/Parser.java rename to src/main/java/hirehive/address/logic/parser/Parser.java index d6551ad8e3f..e084684bb48 100644 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ b/src/main/java/hirehive/address/logic/parser/Parser.java @@ -1,7 +1,7 @@ -package seedu.address.logic.parser; +package hirehive.address.logic.parser; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import hirehive.address.logic.commands.Command; +import hirehive.address.logic.parser.exceptions.ParseException; /** * Represents a Parser that is able to parse user input into a {@code Command} of type {@code T}. diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/hirehive/address/logic/parser/ParserUtil.java similarity index 50% rename from src/main/java/seedu/address/logic/parser/ParserUtil.java rename to src/main/java/hirehive/address/logic/parser/ParserUtil.java index b117acb9c55..357d4edbe8e 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/hirehive/address/logic/parser/ParserUtil.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package hirehive.address.logic.parser; import static java.util.Objects.requireNonNull; @@ -6,21 +6,27 @@ 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; +import hirehive.address.commons.core.index.Index; +import hirehive.address.commons.util.StringUtil; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.person.Address; +import hirehive.address.model.person.Email; +import hirehive.address.model.person.InterviewDate; +import hirehive.address.model.person.Name; +import hirehive.address.model.person.Note; +import hirehive.address.model.person.Phone; +import hirehive.address.model.person.Role; +import hirehive.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."; + public static final String MESSAGE_INVALID_INDEX = "Input is not a non-zero unsigned integer."; + public static final String MESSAGE_INVALID_DAYS = "Input is not a positive integer."; + public static final String MESSAGE_OUT_OF_RANGE = "Number is out of integer range!\n" + + "(must be within -2^31 to 2^31 inclusive)"; /** * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be @@ -29,12 +35,29 @@ public class ParserUtil { */ public static Index parseIndex(String oneBasedIndex) throws ParseException { String trimmedIndex = oneBasedIndex.trim(); - if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { + if (!StringUtil.isValidStringOrInteger(trimmedIndex)) { + throw new ParseException(MESSAGE_OUT_OF_RANGE); + } else if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { throw new ParseException(MESSAGE_INVALID_INDEX); } return Index.fromOneBased(Integer.parseInt(trimmedIndex)); } + /** + * Parses {@code days} into an {@code int} and returns it. Leading and trailing whitespaces will be + * trimmed. + * @throws ParseException if the specified days are invalid (not positive integer) + */ + public static int parseDays(String days) throws ParseException { + String trimmedDays = days.trim(); + if (!StringUtil.isValidStringOrInteger(trimmedDays)) { + throw new ParseException(MESSAGE_OUT_OF_RANGE); + } else if (!StringUtil.isPositiveInteger(trimmedDays)) { + throw new ParseException(MESSAGE_INVALID_DAYS); + } + return Integer.parseInt(trimmedDays); + } + /** * Parses a {@code String name} into a {@code Name}. * Leading and trailing whitespaces will be trimmed. @@ -95,6 +118,21 @@ public static Email parseEmail(String email) throws ParseException { return new Email(trimmedEmail); } + /** + * Parses a {@code String role} into an {@code Role}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code role} is invalid. + */ + public static Role parseRole(String role) throws ParseException { + requireNonNull(role); + String trimmedRole = role.trim(); + if (!Role.isValidRole(trimmedRole)) { + throw new ParseException(Role.MESSAGE_CONSTRAINTS); + } + return new Role(trimmedRole); + } + /** * Parses a {@code String tag} into a {@code Tag}. * Leading and trailing whitespaces will be trimmed. @@ -103,11 +141,21 @@ public static Email parseEmail(String email) throws ParseException { */ public static Tag parseTag(String tag) throws ParseException { requireNonNull(tag); - String trimmedTag = tag.trim(); - if (!Tag.isValidTagName(trimmedTag)) { + String adjustedTag = tag.trim().toLowerCase(); + switch (adjustedTag) { + case "applicant": + return Tag.APPLICANT; + case "candidate": + return Tag.CANDIDATE; + case "interviewee": + return Tag.INTERVIEWEE; + case "offered": + return Tag.OFFERED; + case "rejected": + return Tag.REJECTED; + default: throw new ParseException(Tag.MESSAGE_CONSTRAINTS); } - return new Tag(trimmedTag); } /** @@ -121,4 +169,34 @@ public static Set parseTags(Collection tags) throws ParseException } return tagSet; } + + /** + * Parses a {@code String note} into a {@code Note}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code note} is invalid. + */ + public static Note parseNote(String note) throws ParseException { + requireNonNull(note); + String trimmedNote = note.trim(); + if (!Note.isValidNote(trimmedNote)) { + throw new ParseException(Note.MESSAGE_CONSTRAINTS); + } + return new Note(trimmedNote); + } + + /** + * Parses a {@code String date} into a {@code InterviewDate}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code date} is invalid. + */ + public static InterviewDate parseDate(String date) throws ParseException { + requireNonNull(date); + String trimmedDate = date.trim(); + if (!InterviewDate.isValidDate(trimmedDate)) { + throw new ParseException(InterviewDate.MESSAGE_CONSTRAINTS); + } + return new InterviewDate(trimmedDate); + } } diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/hirehive/address/logic/parser/Prefix.java similarity index 95% rename from src/main/java/seedu/address/logic/parser/Prefix.java rename to src/main/java/hirehive/address/logic/parser/Prefix.java index 348b7686c8a..6d28f904b5b 100644 --- a/src/main/java/seedu/address/logic/parser/Prefix.java +++ b/src/main/java/hirehive/address/logic/parser/Prefix.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package hirehive.address.logic.parser; /** * A prefix that marks the beginning of an argument in an arguments string. diff --git a/src/main/java/hirehive/address/logic/parser/ReminderCommandParser.java b/src/main/java/hirehive/address/logic/parser/ReminderCommandParser.java new file mode 100644 index 00000000000..82e49a5bdd2 --- /dev/null +++ b/src/main/java/hirehive/address/logic/parser/ReminderCommandParser.java @@ -0,0 +1,37 @@ +package hirehive.address.logic.parser; + +import static hirehive.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_TAG; +import static java.util.Objects.requireNonNull; + +import hirehive.address.logic.commands.EditCommand; +import hirehive.address.logic.commands.FilterCommand; +import hirehive.address.logic.commands.ReminderCommand; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.person.PersonContainsTagPredicate; +import hirehive.address.model.person.UpcomingInterviewPredicate; +import hirehive.address.model.tag.Tag; + +/** + * Parses input arguments and creates a new ReminderCommand object + */ +public class ReminderCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ReminderCommand + * and returns a ReminderCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ReminderCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args); + int days; + + if (argMultimap.getPreamble().trim().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ReminderCommand.MESSAGE_USAGE)); + } else { + days = ParserUtil.parseDays(argMultimap.getPreamble()); + } + return new ReminderCommand(new UpcomingInterviewPredicate(days)); + } +} diff --git a/src/main/java/hirehive/address/logic/parser/ScheduleCommandParser.java b/src/main/java/hirehive/address/logic/parser/ScheduleCommandParser.java new file mode 100644 index 00000000000..5792532a710 --- /dev/null +++ b/src/main/java/hirehive/address/logic/parser/ScheduleCommandParser.java @@ -0,0 +1,70 @@ +package hirehive.address.logic.parser; + +import static hirehive.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_DATE; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NAME; +import static java.util.Objects.requireNonNull; + +import java.time.LocalDate; + +import hirehive.address.commons.core.index.Index; +import hirehive.address.logic.commands.EditCommand; +import hirehive.address.logic.commands.ScheduleCommand; +import hirehive.address.logic.commands.queries.NameQuery; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.person.InterviewDate; +import hirehive.address.model.person.NameContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new DateCommand object + */ +public class ScheduleCommandParser implements Parser { + public static final String MESSAGE_DATE_OUT_OF_BOUNDS = "The given date has already passed." + + "Please provide a valid date."; + + /** + * Parses the given {@code String} of arguments in the context of the DateCommand + * and returns a DateCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ScheduleCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_DATE); + NameQuery nameQuery = null; + Index index = null; + InterviewDate date = null; + + if (argMultimap.getValue(PREFIX_NAME).orElse("").trim().isEmpty() + && argMultimap.getPreamble().trim().isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ScheduleCommand.MESSAGE_USAGE)); + } + if (argMultimap.getPreamble().trim().isEmpty()) { + String name = argMultimap.getValue(PREFIX_NAME).get(); + nameQuery = new NameQuery(new NameContainsKeywordsPredicate(name)); + } else { + index = ParserUtil.parseIndex(argMultimap.getPreamble().trim()); + } + if (!argMultimap.getValue(PREFIX_DATE).orElse("").trim().isEmpty()) { + date = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get()); + if (date.getValue().get().isBefore(LocalDate.now())) { + throw new ParseException(MESSAGE_DATE_OUT_OF_BOUNDS); + } + } + if (date == null) { + if (index == null) { + return new ScheduleCommand(nameQuery); + } else { + return new ScheduleCommand(index); + } + } else { + EditCommand.EditPersonDescriptor editPersonDescriptor = new EditCommand.EditPersonDescriptor(); + editPersonDescriptor.setDate(date); + if (index == null) { + return new ScheduleCommand(nameQuery, editPersonDescriptor); + } else { + return new ScheduleCommand(index, editPersonDescriptor); + } + } + } +} diff --git a/src/main/java/hirehive/address/logic/parser/TagCommandParser.java b/src/main/java/hirehive/address/logic/parser/TagCommandParser.java new file mode 100644 index 00000000000..32650f6cc02 --- /dev/null +++ b/src/main/java/hirehive/address/logic/parser/TagCommandParser.java @@ -0,0 +1,79 @@ +package hirehive.address.logic.parser; + +import static hirehive.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_NAME; +import static hirehive.address.logic.parser.CliSyntax.PREFIX_TAG; +import static java.lang.Math.abs; +import static java.util.Objects.requireNonNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import hirehive.address.commons.core.index.Index; +import hirehive.address.logic.commands.EditCommand.EditPersonDescriptor; +import hirehive.address.logic.commands.TagCommand; +import hirehive.address.logic.commands.queries.NameQuery; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.person.NameContainsKeywordsPredicate; +import hirehive.address.model.tag.Tag; + +/** + * Parses input arguments and creates a new TagCommand object + */ +public class TagCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the TagCommand + * and returns an TagCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public TagCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize( + args, PREFIX_NAME, PREFIX_TAG); + + EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); + + if (argMultimap.getValue(PREFIX_TAG).isEmpty()) { + if (argMultimap.getValue(PREFIX_NAME).isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, TagCommand.MESSAGE_USAGE)); + } + String nameKeywords = argMultimap.getValue(PREFIX_NAME).get(); + NameQuery nameQuery = new NameQuery(new NameContainsKeywordsPredicate(nameKeywords)); + try { + int offset = Integer.parseInt(argMultimap.getPreamble()); + if (abs(offset) > 4) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, TagCommand.MESSAGE_TAG_INVALID_OFFSET) + ); + } + return new TagCommand(nameQuery, offset); + } catch (NumberFormatException e) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, TagCommand.MESSAGE_USAGE)); + } + } + + editPersonDescriptor.setTag(ParserUtil.parseTag(argMultimap.getValue(PREFIX_TAG).get())); + + if (argMultimap.getValue(PREFIX_NAME).isEmpty()) { + Index index; + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + return new TagCommand(index, editPersonDescriptor); + } catch (ParseException e) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, TagCommand.MESSAGE_USAGE)); + } + } + + String nameKeywords = argMultimap.getValue(PREFIX_NAME).get(); + NameQuery nameQuery = new NameQuery(new NameContainsKeywordsPredicate(nameKeywords)); + + return new TagCommand(nameQuery, editPersonDescriptor); + } +} diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/hirehive/address/logic/parser/exceptions/ParseException.java similarity index 72% rename from src/main/java/seedu/address/logic/parser/exceptions/ParseException.java rename to src/main/java/hirehive/address/logic/parser/exceptions/ParseException.java index 158a1a54c1c..b8bf2d5569b 100644 --- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java +++ b/src/main/java/hirehive/address/logic/parser/exceptions/ParseException.java @@ -1,6 +1,6 @@ -package seedu.address.logic.parser.exceptions; +package hirehive.address.logic.parser.exceptions; -import seedu.address.commons.exceptions.IllegalValueException; +import hirehive.address.commons.exceptions.IllegalValueException; /** * Represents a parse error encountered by a parser. diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/hirehive/address/model/AddressBook.java similarity index 94% rename from src/main/java/seedu/address/model/AddressBook.java rename to src/main/java/hirehive/address/model/AddressBook.java index 73397161e84..6fcd97b988f 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/hirehive/address/model/AddressBook.java @@ -1,13 +1,13 @@ -package seedu.address.model; +package hirehive.address.model; import static java.util.Objects.requireNonNull; import java.util.List; +import hirehive.address.commons.util.ToStringBuilder; +import hirehive.address.model.person.Person; +import hirehive.address.model.person.UniquePersonList; import javafx.collections.ObservableList; -import seedu.address.commons.util.ToStringBuilder; -import seedu.address.model.person.Person; -import seedu.address.model.person.UniquePersonList; /** * Wraps all data at the address-book level diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/hirehive/address/model/Model.java similarity index 73% rename from src/main/java/seedu/address/model/Model.java rename to src/main/java/hirehive/address/model/Model.java index d54df471c1f..9a995d8f07d 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/hirehive/address/model/Model.java @@ -1,11 +1,14 @@ -package seedu.address.model; +package hirehive.address.model; import java.nio.file.Path; +import java.time.LocalDate; import java.util.function.Predicate; +import hirehive.address.commons.core.GuiSettings; +import hirehive.address.model.person.InterviewDate; +import hirehive.address.model.person.Note; +import hirehive.address.model.person.Person; import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.Person; /** * The API of the Model component. @@ -84,4 +87,33 @@ public interface Model { * @throws NullPointerException if {@code predicate} is null. */ void updateFilteredPersonList(Predicate predicate); + + void unfilterPersonList(); + + /** + * Sorts the filtered person list by their interview date. + */ + void sortPersons(); + + /** + * Resets the sorted list when other commands are typed. + */ + void resetSorting(); + + /** + * Updates the notes of the person. + */ + void updatePersonNote(Person person); + + /** + * Returns the notes of the person. + */ + Note getPersonNote(); + + int getListSize(); + + /** + * Returns the next available date for an interview from the current date. + */ + InterviewDate getAvailableDate(); } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/hirehive/address/model/ModelManager.java similarity index 54% rename from src/main/java/seedu/address/model/ModelManager.java rename to src/main/java/hirehive/address/model/ModelManager.java index 57bc563fde6..38753ad1c84 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/hirehive/address/model/ModelManager.java @@ -1,17 +1,26 @@ -package seedu.address.model; +package hirehive.address.model; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; import java.nio.file.Path; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.ResolverStyle; +import java.util.Comparator; +import java.util.List; import java.util.function.Predicate; import java.util.logging.Logger; - +import java.util.stream.Stream; + +import hirehive.address.commons.core.GuiSettings; +import hirehive.address.commons.core.LogsCenter; +import hirehive.address.commons.util.CollectionUtil; +import hirehive.address.model.person.InterviewDate; +import hirehive.address.model.person.Note; +import hirehive.address.model.person.Person; import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; +import javafx.collections.transformation.SortedList; /** * Represents the in-memory model of the address book data. @@ -22,18 +31,25 @@ public class ModelManager implements Model { private final AddressBook addressBook; private final UserPrefs userPrefs; private final FilteredList filteredPersons; + private Note personNote; + private final SortedList sortedPersons; + private boolean isSorted = false; + + private Predicate currFilter = PREDICATE_SHOW_ALL_PERSONS; /** * Initializes a ModelManager with the given addressBook and userPrefs. */ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { - requireAllNonNull(addressBook, userPrefs); + CollectionUtil.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()); + personNote = new Note(Note.DEFAULT_NOTE); + this.sortedPersons = new SortedList<>(filteredPersons); } public ModelManager() { @@ -101,12 +117,12 @@ public void deletePerson(Person target) { @Override public void addPerson(Person person) { addressBook.addPerson(person); - updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + unfilterPersonList(); } @Override public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); + CollectionUtil.requireAllNonNull(target, editedPerson); addressBook.setPerson(target, editedPerson); } @@ -119,13 +135,81 @@ public void setPerson(Person target, Person editedPerson) { */ @Override public ObservableList getFilteredPersonList() { - return filteredPersons; + return sortedPersons; } @Override public void updateFilteredPersonList(Predicate predicate) { requireNonNull(predicate); - filteredPersons.setPredicate(predicate); + currFilter = currFilter.and(predicate); + filteredPersons.setPredicate(currFilter); + resetSorting(); + } + + @Override + public void unfilterPersonList() { + currFilter = PREDICATE_SHOW_ALL_PERSONS; + filteredPersons.setPredicate(PREDICATE_SHOW_ALL_PERSONS); + resetSorting(); + } + + @Override + public void sortPersons() { + Comparator comparator = Comparator.comparing( + person -> person.getDate().getValue().orElse(LocalDate.MAX) + ); + sortedPersons.setComparator(comparator); + isSorted = true; + } + + @Override + public void resetSorting() { + sortedPersons.setComparator(null); + isSorted = false; + } + + + @Override + public void updatePersonNote(Person person) { + personNote = person.getNote(); + } + + @Override + public Note getPersonNote() { + return personNote; + } + + @Override + public int getListSize() { + return filteredPersons.size(); + } + + /** + * Returns the next available date for an interview starting from the next day. + * @return An {@code InterviewDate} object + */ + @Override + public InterviewDate getAvailableDate() { + List sortedAllDates = this.addressBook.getPersonList().stream() + .map(person -> person.getDate().getValue().orElse(LocalDate.MAX)) + .sorted(LocalDate::compareTo).toList(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/uuuu") + .withResolverStyle(ResolverStyle.STRICT); + LocalDate iterationDate = LocalDate.now().plusDays(1); + boolean bContinue = true; + int i = 0; + while (i < sortedAllDates.size() && sortedAllDates.get(i) != LocalDate.MAX && bContinue) { + LocalDate currDate = sortedAllDates.get(i); + if (currDate.isBefore(iterationDate)) { + i++; + } else if (currDate.isAfter(iterationDate)) { + bContinue = false; + } else { + iterationDate = iterationDate.plusDays(1); + i++; + } + } + return new InterviewDate(iterationDate.format(formatter)); } @Override diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/hirehive/address/model/ReadOnlyAddressBook.java similarity index 80% rename from src/main/java/seedu/address/model/ReadOnlyAddressBook.java rename to src/main/java/hirehive/address/model/ReadOnlyAddressBook.java index 6ddc2cd9a29..6404a7728f6 100644 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ b/src/main/java/hirehive/address/model/ReadOnlyAddressBook.java @@ -1,7 +1,7 @@ -package seedu.address.model; +package hirehive.address.model; +import hirehive.address.model.person.Person; import javafx.collections.ObservableList; -import seedu.address.model.person.Person; /** * Unmodifiable view of an address book diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/hirehive/address/model/ReadOnlyUserPrefs.java similarity index 69% rename from src/main/java/seedu/address/model/ReadOnlyUserPrefs.java rename to src/main/java/hirehive/address/model/ReadOnlyUserPrefs.java index befd58a4c73..583737cf8eb 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/hirehive/address/model/ReadOnlyUserPrefs.java @@ -1,8 +1,8 @@ -package seedu.address.model; +package hirehive.address.model; import java.nio.file.Path; -import seedu.address.commons.core.GuiSettings; +import hirehive.address.commons.core.GuiSettings; /** * Unmodifiable view of user prefs. diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/hirehive/address/model/UserPrefs.java similarity index 96% rename from src/main/java/seedu/address/model/UserPrefs.java rename to src/main/java/hirehive/address/model/UserPrefs.java index 6be655fb4c7..1204e610030 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/hirehive/address/model/UserPrefs.java @@ -1,4 +1,4 @@ -package seedu.address.model; +package hirehive.address.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 hirehive.address.commons.core.GuiSettings; /** * Represents User's preferences. diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/hirehive/address/model/person/Address.java similarity index 89% rename from src/main/java/seedu/address/model/person/Address.java rename to src/main/java/hirehive/address/model/person/Address.java index 469a2cc9a1e..ec973db7cb6 100644 --- a/src/main/java/seedu/address/model/person/Address.java +++ b/src/main/java/hirehive/address/model/person/Address.java @@ -1,7 +1,8 @@ -package seedu.address.model.person; +package hirehive.address.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; + +import hirehive.address.commons.util.AppUtil; /** * Represents a Person's address in the address book. @@ -26,7 +27,7 @@ public class Address { */ public Address(String address) { requireNonNull(address); - checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS); + AppUtil.checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS); value = address; } diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/hirehive/address/model/person/Email.java similarity index 92% rename from src/main/java/seedu/address/model/person/Email.java rename to src/main/java/hirehive/address/model/person/Email.java index c62e512bc29..2968c371968 100644 --- a/src/main/java/seedu/address/model/person/Email.java +++ b/src/main/java/hirehive/address/model/person/Email.java @@ -1,7 +1,9 @@ -package seedu.address.model.person; +package hirehive.address.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; + +import hirehive.address.commons.util.AppUtil; + /** * Represents a Person's email in the address book. @@ -16,7 +18,7 @@ public class Email { + "the parentheses, (" + SPECIAL_CHARACTERS + "). The local-part may not start or end with any special " + "characters.\n" + "2. This is followed by a '@' and then a domain name. The domain name is made up of domain labels " - + "separated by periods.\n" + + "that may be separated by periods, if any.\n" + "The domain name must:\n" + " - end with a domain label at least 2 characters long\n" + " - have each domain label start and end with alphanumeric characters\n" @@ -40,7 +42,7 @@ public class Email { */ public Email(String email) { requireNonNull(email); - checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS); + AppUtil.checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS); value = email; } diff --git a/src/main/java/hirehive/address/model/person/InterviewDate.java b/src/main/java/hirehive/address/model/person/InterviewDate.java new file mode 100644 index 00000000000..bc3d43a57fd --- /dev/null +++ b/src/main/java/hirehive/address/model/person/InterviewDate.java @@ -0,0 +1,91 @@ +package hirehive.address.model.person; + +import static java.util.Objects.requireNonNull; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.format.ResolverStyle; +import java.util.Optional; + +import hirehive.address.commons.util.AppUtil; + +/** + * Represents a Person's interview date in the address book + * Can be empty. Represented in the string format of DD/MM/YYYY + * Guarantees: immutable; is valid as declared in {@link #isValidDate(String)} + */ +public class InterviewDate { + public static final String MESSAGE_CONSTRAINTS = "Please provide a valid date in the DD/MM/YYYY format"; + public static final String DEFAULT_DATE = "01/01/2025"; + + public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("dd/MM/uuuu") + .withResolverStyle(ResolverStyle.STRICT); + + public final Optional value; + + public InterviewDate() { + value = Optional.empty(); + } + + /** + * Constructor for new {@link InterviewDate} objects with initialized dates + * @param date Date value + */ + public InterviewDate(String date) { + requireNonNull(date); + AppUtil.checkArgument(isValidDate(date), MESSAGE_CONSTRAINTS); + if (!date.isEmpty()) { + value = Optional.of(LocalDate.parse(date, DATE_TIME_FORMATTER)); + } else { + value = Optional.empty(); + } + } + + /** + * Returns true if a given string is a valid date. + */ + public static boolean isValidDate(String test) { + if (test.isEmpty()) { + return true; + } + try { + LocalDate.parse(test, DATE_TIME_FORMATTER); + return true; + } catch (DateTimeParseException e) { + return false; + } + } + + /** + * Returns the value of the interview date, or empty if not set. + */ + public Optional getValue() { + return value; + } + + @Override + public String toString() { + return value.map(x -> x.format(DATE_TIME_FORMATTER)).orElse(""); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof InterviewDate)) { + return false; + } + + InterviewDate otherDate = (InterviewDate) other; + return value.equals(otherDate.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/hirehive/address/model/person/Name.java similarity index 70% rename from src/main/java/seedu/address/model/person/Name.java rename to src/main/java/hirehive/address/model/person/Name.java index 173f15b9b00..0f087e3a0fa 100644 --- a/src/main/java/seedu/address/model/person/Name.java +++ b/src/main/java/hirehive/address/model/person/Name.java @@ -1,7 +1,8 @@ -package seedu.address.model.person; +package hirehive.address.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; + +import hirehive.address.commons.util.AppUtil; /** * Represents a Person's name in the address book. @@ -10,13 +11,15 @@ public class Name { public static final String MESSAGE_CONSTRAINTS = - "Names should only contain alphanumeric characters and spaces, and it should not be blank"; + "Names should only contain English letters, spaces, and the following symbols:\n" + + " , ( ) / . @ - '\n" + + "Names should not be blank or start with a symbol."; /* * The first character of the address must not be a whitespace, * otherwise " " (a blank string) becomes a valid input. */ - public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; + public static final String VALIDATION_REGEX = "[A-Za-z][A-Za-z ,()/.@'-]*"; public final String fullName; @@ -27,7 +30,7 @@ public class Name { */ public Name(String name) { requireNonNull(name); - checkArgument(isValidName(name), MESSAGE_CONSTRAINTS); + AppUtil.checkArgument(isValidName(name), MESSAGE_CONSTRAINTS); fullName = name; } @@ -56,7 +59,7 @@ public boolean equals(Object other) { } Name otherName = (Name) other; - return fullName.equals(otherName.fullName); + return fullName.equalsIgnoreCase(otherName.fullName); } @Override diff --git a/src/main/java/hirehive/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/hirehive/address/model/person/NameContainsKeywordsPredicate.java new file mode 100644 index 00000000000..9ac303f278d --- /dev/null +++ b/src/main/java/hirehive/address/model/person/NameContainsKeywordsPredicate.java @@ -0,0 +1,46 @@ +package hirehive.address.model.person; + +import hirehive.address.commons.util.StringUtil; +import hirehive.address.commons.util.ToStringBuilder; +import hirehive.address.logic.Messages; + +/** + * Tests that a {@code Person}'s {@code Name} matches any of the keyword given. + */ +public class NameContainsKeywordsPredicate implements PersonPredicate { + private final String keyword; + + public NameContainsKeywordsPredicate(String keyword) { + this.keyword = keyword; + } + + @Override + public boolean test(Person person) { + return StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof NameContainsKeywordsPredicate)) { + return false; + } + + NameContainsKeywordsPredicate otherNameContainsKeywordsPredicate = (NameContainsKeywordsPredicate) other; + return keyword.equals(otherNameContainsKeywordsPredicate.keyword); + } + + @Override + public String toString() { + return new ToStringBuilder(this).add("keywords", keyword).toString(); + } + + @Override + public String getSuccessString() { + return String.format(Messages.MESSAGE_FILTER_OVERVIEW_NAME, keyword); + } +} diff --git a/src/main/java/hirehive/address/model/person/Note.java b/src/main/java/hirehive/address/model/person/Note.java new file mode 100644 index 00000000000..7330ba2174e --- /dev/null +++ b/src/main/java/hirehive/address/model/person/Note.java @@ -0,0 +1,67 @@ +package hirehive.address.model.person; + +import static java.util.Objects.requireNonNull; + +import hirehive.address.commons.util.AppUtil; + +/** + * Represents a Person's note in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidNote(String)} + */ +public class Note { + + public static final String MESSAGE_CONSTRAINTS = "Notes can take any values, but are limited to 500 characters"; + public static final String DEFAULT_NOTE = ""; + + public final String value; + + /** + * Constructs an {@code Notes}. + * + * @param note A valid note. + */ + public Note(String note) { + requireNonNull(note); + AppUtil.checkArgument(isValidNote(note), MESSAGE_CONSTRAINTS); + value = note; + } + + /** + * Returns true if a given string is a valid note. + */ + public static boolean isValidNote(String test) { + return test.length() <= 500; + } + + /** + * Returns true if the note is empty. + */ + public boolean isEmpty() { + return value.isEmpty(); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Note)) { + return false; + } + + Note otherNote = (Note) other; + return value.equals(otherNote.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/hirehive/address/model/person/Person.java b/src/main/java/hirehive/address/model/person/Person.java new file mode 100644 index 00000000000..b114603a587 --- /dev/null +++ b/src/main/java/hirehive/address/model/person/Person.java @@ -0,0 +1,172 @@ +package hirehive.address.model.person; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import hirehive.address.commons.util.CollectionUtil; +import hirehive.address.commons.util.ToStringBuilder; +import hirehive.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 Role role; + private final Tag tag; + private final Note note; + private final InterviewDate date; + + /** + * Every field must be present. + */ + public Person(Name name, Phone phone, Email email, Address address, Role role, Tag tag, Note note, + InterviewDate date) { + CollectionUtil.requireAllNonNull(name, phone, email, address, role, tag, note); + this.name = name; + this.phone = phone; + this.email = email; + this.address = address; + this.role = role; + this.tag = tag; + this.note = note; + this.date = date; + } + + /** + * Method used to construct a default Person object without a note. + */ + public static Person createDefaultPerson(Name name, Phone phone, Email email, Address address, Role role) { + return new Person(name, phone, email, address, role, Tag.getDefaultTag(), new Note(Note.DEFAULT_NOTE), + new InterviewDate()); + } + + /** + * Method used to construct a default Person object with a note. + */ + public static Person addDefaultPersonWithNote(Name name, Phone phone, Email email, Address address, Role role, + Note note) { + return new Person(name, phone, email, address, role, Tag.getDefaultTag(), note, + new InterviewDate()); + } + + public Name getName() { + return name; + } + + public Phone getPhone() { + return phone; + } + + public Email getEmail() { + return email; + } + + public Address getAddress() { + return address; + } + + public Role getRole() { + return role; + } + + public Note getNote() { + return note; + } + + public InterviewDate getDate() { + return date; + } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public Tag getTag() { + return tag; + } + + /** + * Returns true if both persons have the same name, ignoring case differences + * and ignores number of spacing in name. + * + * @param otherPerson The other person to compare to. + * @return true if the names are equivalent after normalization; false otherwise. + */ + public boolean isSamePerson(Person otherPerson) { + if (otherPerson == this) { + return true; + } + + return otherPerson != null + && normalizeName(otherPerson.getName()).equalsIgnoreCase(normalizeName(getName())); + } + + /** + * Normalizes a name by trimming leading/trailing spaces and + * replacing multiple consecutive whitespace characters with a single space. + * + * @param name The name to normalize. + * @return The normalized name. + */ + private String normalizeName(Name name) { + return name.toString().trim().replaceAll("\\s+", " "); + } + + /** + * 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; + } + + // instanceof handles nulls + if (!(other instanceof Person)) { + return false; + } + + Person otherPerson = (Person) other; + return name.equals(otherPerson.name) + && phone.equals(otherPerson.phone) + && email.equals(otherPerson.email) + && address.equals(otherPerson.address) + && role.equals(otherPerson.role) + && tag.equals(otherPerson.tag) + && note.equals(otherPerson.note) + && date.equals(otherPerson.date); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name, phone, email, address, role, tag, note, date); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("name", name) + .add("phone", phone) + .add("email", email) + .add("address", address) + .add("role", role) + .add("tag", tag) + .add("note", note) + .add("interviewDate", date) + .toString(); + } + +} diff --git a/src/main/java/hirehive/address/model/person/PersonContainsTagPredicate.java b/src/main/java/hirehive/address/model/person/PersonContainsTagPredicate.java new file mode 100644 index 00000000000..3f49f1b67e7 --- /dev/null +++ b/src/main/java/hirehive/address/model/person/PersonContainsTagPredicate.java @@ -0,0 +1,47 @@ +package hirehive.address.model.person; + +import java.util.function.Predicate; + +import hirehive.address.commons.util.ToStringBuilder; +import hirehive.address.logic.Messages; +import hirehive.address.model.tag.Tag; + +/** + * Tests that a {@code Person}'s {@code Tag}s matches the given Tag. + */ +public class PersonContainsTagPredicate implements PersonPredicate { + private final Tag tag; + public PersonContainsTagPredicate(Tag tag) { + this.tag = tag; + } + + @Override + public boolean test(Person person) { + return person.getTag().equals(tag); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof PersonContainsTagPredicate)) { + return false; + } + + PersonContainsTagPredicate otherPersonContainsTagPredicate = (PersonContainsTagPredicate) other; + return this.tag.equals(otherPersonContainsTagPredicate.tag); + } + + @Override + public String toString() { + return new ToStringBuilder(this).add("tag", this.tag).toString(); + } + + @Override + public String getSuccessString() { + return String.format(Messages.MESSAGE_FILTER_OVERVIEW_TAG, tag); + } +} diff --git a/src/main/java/hirehive/address/model/person/PersonDoesNotContainTagPredicate.java b/src/main/java/hirehive/address/model/person/PersonDoesNotContainTagPredicate.java new file mode 100644 index 00000000000..e914785f1b6 --- /dev/null +++ b/src/main/java/hirehive/address/model/person/PersonDoesNotContainTagPredicate.java @@ -0,0 +1,47 @@ +package hirehive.address.model.person; + +import hirehive.address.commons.util.ToStringBuilder; +import hirehive.address.logic.Messages; +import hirehive.address.model.tag.Tag; + +/** + * Tests that a Person's {@code tag}s do not match the given tag + */ +public class PersonDoesNotContainTagPredicate implements PersonPredicate { + private final Tag tag; + + public PersonDoesNotContainTagPredicate(Tag tag) { + this.tag = tag; + } + + @Override + public boolean test(Person person) { + return !person.getTag().equals(tag); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof PersonDoesNotContainTagPredicate)) { + return false; + } + + PersonDoesNotContainTagPredicate otherPersonDoesNotContainTagPredicate = + (PersonDoesNotContainTagPredicate) other; + return this.tag.equals(otherPersonDoesNotContainTagPredicate.tag); + } + + @Override + public String toString() { + return new ToStringBuilder(this).add("tag", this.tag).toString(); + } + + @Override + public String getSuccessString() { + return String.format(Messages.MESSAGE_FILTEROUT_OVERVIEW_TAG, tag); + } +} diff --git a/src/main/java/hirehive/address/model/person/PersonPredicate.java b/src/main/java/hirehive/address/model/person/PersonPredicate.java new file mode 100644 index 00000000000..e39d29ca506 --- /dev/null +++ b/src/main/java/hirehive/address/model/person/PersonPredicate.java @@ -0,0 +1,10 @@ +package hirehive.address.model.person; + +import java.util.function.Predicate; + +/** + * Interface used for predicates that operate on the Person class + */ +public interface PersonPredicate extends Predicate { + public String getSuccessString(); +} diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/hirehive/address/model/person/Phone.java similarity index 81% rename from src/main/java/seedu/address/model/person/Phone.java rename to src/main/java/hirehive/address/model/person/Phone.java index d733f63d739..f42ea27ad9a 100644 --- a/src/main/java/seedu/address/model/person/Phone.java +++ b/src/main/java/hirehive/address/model/person/Phone.java @@ -1,7 +1,8 @@ -package seedu.address.model.person; +package hirehive.address.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; + +import hirehive.address.commons.util.AppUtil; /** * Represents a Person's phone number in the address book. @@ -11,8 +12,8 @@ public class Phone { public static final String MESSAGE_CONSTRAINTS = - "Phone numbers should only contain numbers, and it should be at least 3 digits long"; - public static final String VALIDATION_REGEX = "\\d{3,}"; + "Phone numbers should only contain numbers, and it should be an 8 digit number starting with 8, 9 or 6"; + public static final String VALIDATION_REGEX = "[896]\\d{7}"; public final String value; /** @@ -22,7 +23,7 @@ public class Phone { */ public Phone(String phone) { requireNonNull(phone); - checkArgument(isValidPhone(phone), MESSAGE_CONSTRAINTS); + AppUtil.checkArgument(isValidPhone(phone), MESSAGE_CONSTRAINTS); value = phone; } diff --git a/src/main/java/hirehive/address/model/person/Role.java b/src/main/java/hirehive/address/model/person/Role.java new file mode 100644 index 00000000000..88e93b1c0c3 --- /dev/null +++ b/src/main/java/hirehive/address/model/person/Role.java @@ -0,0 +1,67 @@ +package hirehive.address.model.person; + +import static java.util.Objects.requireNonNull; + +import hirehive.address.commons.util.AppUtil; + +/** + * Represents a Person's role in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidRole(String)} + */ +public class Role { + public static final String MESSAGE_CONSTRAINTS = + "Roles 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 fullRole; + + /** + * Constructs a {@code Role}. + * + * @param role A valid role. + */ + public Role(String role) { + requireNonNull(role); + AppUtil.checkArgument(isValidRole(role), MESSAGE_CONSTRAINTS); + fullRole = role; + } + + /** + * Returns true if a given string is a valid role. + * + * @param test The string to test. + * @return true if the string matches the role name format; false otherwise. + */ + public static boolean isValidRole(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return fullRole; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Role)) { + return false; + } + + Role otherRole = (Role) other; + return fullRole.equals(otherRole.fullRole); + } + + @Override + public int hashCode() { + return fullRole.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/hirehive/address/model/person/UniquePersonList.java similarity index 92% rename from src/main/java/seedu/address/model/person/UniquePersonList.java rename to src/main/java/hirehive/address/model/person/UniquePersonList.java index cc0a68d79f9..8242462dc46 100644 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ b/src/main/java/hirehive/address/model/person/UniquePersonList.java @@ -1,15 +1,15 @@ -package seedu.address.model.person; +package hirehive.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 hirehive.address.commons.util.CollectionUtil; +import hirehive.address.model.person.exceptions.DuplicatePersonException; +import hirehive.address.model.person.exceptions.PersonNotFoundException; 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. @@ -54,7 +54,7 @@ public void add(Person toAdd) { * 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); + CollectionUtil.requireAllNonNull(target, editedPerson); int index = internalList.indexOf(target); if (index == -1) { @@ -89,7 +89,7 @@ public void setPersons(UniquePersonList replacement) { * {@code persons} must not contain duplicate persons. */ public void setPersons(List persons) { - requireAllNonNull(persons); + CollectionUtil.requireAllNonNull(persons); if (!personsAreUnique(persons)) { throw new DuplicatePersonException(); } diff --git a/src/main/java/hirehive/address/model/person/UpcomingInterviewPredicate.java b/src/main/java/hirehive/address/model/person/UpcomingInterviewPredicate.java new file mode 100644 index 00000000000..f10b2b4d7dc --- /dev/null +++ b/src/main/java/hirehive/address/model/person/UpcomingInterviewPredicate.java @@ -0,0 +1,62 @@ +package hirehive.address.model.person; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; + +import hirehive.address.logic.Messages; + +/** + * Tests that a {@code Person}'s {@code InterviewDate} is within the given amount of days from the current date. + */ +public class UpcomingInterviewPredicate implements PersonPredicate { + public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("dd/MM/yyyy"); + private final int days; + private final LocalDate currDay; + + /** + * Default constructor for {@code UpcomingInterviewPredicate} + * @param days Number of days to query for + */ + public UpcomingInterviewPredicate(int days) { + currDay = LocalDate.now(); + this.days = days; + } + + /** + * Testing constructor for {@code UpcomingInterviewPredicate}, allows setting of current day to given value + * @param days Number of days to query for + * @param currDay Initialized date to query from + */ + public UpcomingInterviewPredicate(int days, String currDay) { + this.currDay = LocalDate.parse(currDay, DATE_TIME_FORMATTER); + this.days = days; + } + + @Override + public boolean test(Person person) { + return person.getDate().value.map(date -> ChronoUnit.DAYS.between(currDay, date)) + .map(days -> days <= this.days && days >= 0) + .orElse(false); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof UpcomingInterviewPredicate)) { + return false; + } + + UpcomingInterviewPredicate otherUpcomingInterviewPredicate = (UpcomingInterviewPredicate) other; + return this.days == otherUpcomingInterviewPredicate.days && this.currDay.equals(otherUpcomingInterviewPredicate.currDay); + } + + @Override + public String getSuccessString() { + return String.format(Messages.MESSAGE_FILTER_OVERVIEW_DATE, days); + } +} diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/hirehive/address/model/person/exceptions/DuplicatePersonException.java similarity index 86% rename from src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java rename to src/main/java/hirehive/address/model/person/exceptions/DuplicatePersonException.java index d7290f59442..b2feaa03986 100644 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ b/src/main/java/hirehive/address/model/person/exceptions/DuplicatePersonException.java @@ -1,4 +1,4 @@ -package seedu.address.model.person.exceptions; +package hirehive.address.model.person.exceptions; /** * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/hirehive/address/model/person/exceptions/PersonNotFoundException.java similarity index 74% rename from src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java rename to src/main/java/hirehive/address/model/person/exceptions/PersonNotFoundException.java index fa764426ca7..8c8952cbbf1 100644 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ b/src/main/java/hirehive/address/model/person/exceptions/PersonNotFoundException.java @@ -1,4 +1,4 @@ -package seedu.address.model.person.exceptions; +package hirehive.address.model.person.exceptions; /** * Signals that the operation is unable to find the specified person. diff --git a/src/main/java/hirehive/address/model/tag/Tag.java b/src/main/java/hirehive/address/model/tag/Tag.java new file mode 100644 index 00000000000..4e64cb60974 --- /dev/null +++ b/src/main/java/hirehive/address/model/tag/Tag.java @@ -0,0 +1,63 @@ +package hirehive.address.model.tag; + +/** + * Represents a Tag in the address book. + */ + +public enum Tag { + APPLICANT("Applicant"), + CANDIDATE("Candidate"), + INTERVIEWEE("Interviewee"), + OFFERED("Offered"), + REJECTED("Rejected"); + + public static final String MESSAGE_CONSTRAINTS = "Tag should be one of the following:\n" + + " 1. Applicant\n" + + " 2. Candidate\n" + + " 3. Interviewee\n" + + " 4. Offered\n" + + " 5. Rejected\n"; + + private static final Tag DEFAULT_TAG = APPLICANT; + + public final String tagName; + + Tag(String tagName) { + this.tagName = tagName; + } + + public String getTagName() { + return tagName; + } + + public static Tag getDefaultTag() { + return DEFAULT_TAG; + } + + /** + * Gets the tag of the hiring stage that is offset from this tag + * @param offset of the wanted hiring stage tag from this tag + * @return tag that is offset from this tag + */ + public Tag offsetBy(int offset) { + if (offset == 0) { + return this; + } + if (offset > 0) { + return switch (this) { + case REJECTED -> Tag.APPLICANT.offsetBy(offset - 1); + case APPLICANT -> Tag.CANDIDATE.offsetBy(offset - 1); + case CANDIDATE -> Tag.INTERVIEWEE.offsetBy(offset - 1); + case INTERVIEWEE -> Tag.OFFERED.offsetBy(offset - 1); + case OFFERED -> this; + }; + } + return switch (this) { + case REJECTED -> this; + case APPLICANT -> Tag.REJECTED.offsetBy(offset + 1); + case CANDIDATE -> Tag.APPLICANT.offsetBy(offset + 1); + case INTERVIEWEE -> Tag.CANDIDATE.offsetBy(offset + 1); + case OFFERED -> Tag.INTERVIEWEE.offsetBy(offset + 1); + }; + } +} diff --git a/src/main/java/hirehive/address/model/util/SampleDataUtil.java b/src/main/java/hirehive/address/model/util/SampleDataUtil.java new file mode 100644 index 00000000000..6a749432f1a --- /dev/null +++ b/src/main/java/hirehive/address/model/util/SampleDataUtil.java @@ -0,0 +1,54 @@ +package hirehive.address.model.util; + +import java.util.HashSet; +import java.util.Set; + +import hirehive.address.logic.parser.ParserUtil; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.AddressBook; +import hirehive.address.model.ReadOnlyAddressBook; +import hirehive.address.model.person.Address; +import hirehive.address.model.person.Email; +import hirehive.address.model.person.InterviewDate; +import hirehive.address.model.person.Name; +import hirehive.address.model.person.Note; +import hirehive.address.model.person.Person; +import hirehive.address.model.person.Phone; +import hirehive.address.model.person.Role; +import hirehive.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("Alice Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), + new Address("Blk 30 Geylang Street 29, #06-40"), new Role("UI designer"), + Tag.APPLICANT, new Note("20 years old"), new InterviewDate()), + new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), + new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), new Role("HR manager"), + Tag.INTERVIEWEE, new Note(""), new InterviewDate("01/03/2025")), + 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"), new Role("HR manager"), + Tag.CANDIDATE, new Note(""), new InterviewDate()), + new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), + new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), new Role("Software Engineer"), + Tag.OFFERED, new Note(""), new InterviewDate("06/07/2025")), + new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), + new Address("Blk 47 Tampines Street 20, #17-35"), new Role("Software Engineer"), + Tag.REJECTED, new Note("30 years old"), new InterviewDate("06/03/2025")), + new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), + new Address("Blk 45 Aljunied Street 85, #11-31"), new Role("Cybersecurity specialist"), + Tag.APPLICANT, new Note(""), new InterviewDate()) + }; + } + + public static ReadOnlyAddressBook getSampleAddressBook() { + AddressBook sampleAb = new AddressBook(); + for (Person samplePerson : getSamplePersons()) { + sampleAb.addPerson(samplePerson); + } + return sampleAb; + } +} diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/hirehive/address/storage/AddressBookStorage.java similarity index 82% rename from src/main/java/seedu/address/storage/AddressBookStorage.java rename to src/main/java/hirehive/address/storage/AddressBookStorage.java index f2e015105ae..c393c276c0e 100644 --- a/src/main/java/seedu/address/storage/AddressBookStorage.java +++ b/src/main/java/hirehive/address/storage/AddressBookStorage.java @@ -1,14 +1,15 @@ -package seedu.address.storage; +package hirehive.address.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataLoadingException; -import seedu.address.model.ReadOnlyAddressBook; +import hirehive.address.commons.exceptions.DataLoadingException; +import hirehive.address.model.AddressBook; +import hirehive.address.model.ReadOnlyAddressBook; /** - * Represents a storage for {@link seedu.address.model.AddressBook}. + * Represents a storage for {@link AddressBook}. */ public interface AddressBookStorage { diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/hirehive/address/storage/JsonAdaptedPerson.java similarity index 55% rename from src/main/java/seedu/address/storage/JsonAdaptedPerson.java rename to src/main/java/hirehive/address/storage/JsonAdaptedPerson.java index bd1ca0f56c8..948fc85d17c 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ b/src/main/java/hirehive/address/storage/JsonAdaptedPerson.java @@ -1,4 +1,4 @@ -package seedu.address.storage; +package hirehive.address.storage; import java.util.ArrayList; import java.util.HashSet; @@ -9,13 +9,17 @@ 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; +import hirehive.address.commons.exceptions.IllegalValueException; +import hirehive.address.logic.parser.ParserUtil; +import hirehive.address.model.person.Address; +import hirehive.address.model.person.Email; +import hirehive.address.model.person.InterviewDate; +import hirehive.address.model.person.Name; +import hirehive.address.model.person.Note; +import hirehive.address.model.person.Person; +import hirehive.address.model.person.Phone; +import hirehive.address.model.person.Role; +import hirehive.address.model.tag.Tag; /** * Jackson-friendly version of {@link Person}. @@ -28,7 +32,10 @@ class JsonAdaptedPerson { private final String phone; private final String email; private final String address; - private final List tags = new ArrayList<>(); + private final String role; + private final String tag; + private final String note; + private final String date; /** * Constructs a {@code JsonAdaptedPerson} with the given person details. @@ -36,14 +43,16 @@ class JsonAdaptedPerson { @JsonCreator public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone, @JsonProperty("email") String email, @JsonProperty("address") String address, - @JsonProperty("tags") List tags) { + @JsonProperty("role") String role, @JsonProperty("tag") String tag, + @JsonProperty("note") String note, @JsonProperty("date") String date) { this.name = name; this.phone = phone; this.email = email; this.address = address; - if (tags != null) { - this.tags.addAll(tags); - } + this.role = role; + this.tag = tag; + this.note = note; + this.date = date; } /** @@ -54,9 +63,10 @@ public JsonAdaptedPerson(Person source) { phone = source.getPhone().value; email = source.getEmail().value; address = source.getAddress().value; - tags.addAll(source.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList())); + role = source.getRole().fullRole; + tag = source.getTag().getTagName(); + note = source.getNote().value; + date = source.getDate().toString(); } /** @@ -65,11 +75,6 @@ public JsonAdaptedPerson(Person source) { * @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 : tags) { - personTags.add(tag.toModelType()); - } - if (name == null) { throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); } @@ -102,8 +107,37 @@ public Person toModelType() throws IllegalValueException { } final Address modelAddress = new Address(address); - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); + if (role == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Role.class.getSimpleName())); + } + if (!Role.isValidRole(role)) { + throw new IllegalValueException(Role.MESSAGE_CONSTRAINTS); + } + final Role modelRole = new Role(role); + + if (tag == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Tag.class.getSimpleName())); + } + final Tag modelTag = ParserUtil.parseTag(tag); + + if (note == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Note.class.getSimpleName())); + } + if (!Note.isValidNote(note)) { + throw new IllegalValueException(Note.MESSAGE_CONSTRAINTS); + } + final Note modelNote = new Note(note); + + if (date == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + InterviewDate.class.getSimpleName())); + } + if (!InterviewDate.isValidDate(date)) { + throw new IllegalValueException(InterviewDate.MESSAGE_CONSTRAINTS); + } + final InterviewDate modelDate = new InterviewDate(date); + + return new Person(modelName, modelPhone, modelEmail, modelAddress, modelRole, modelTag, modelNote, modelDate); } } diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/hirehive/address/storage/JsonAdaptedTag.java similarity index 73% rename from src/main/java/seedu/address/storage/JsonAdaptedTag.java rename to src/main/java/hirehive/address/storage/JsonAdaptedTag.java index 0df22bdb754..2a2ee159fb2 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java +++ b/src/main/java/hirehive/address/storage/JsonAdaptedTag.java @@ -1,10 +1,12 @@ -package seedu.address.storage; +package hirehive.address.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 hirehive.address.commons.exceptions.IllegalValueException; +import hirehive.address.logic.parser.ParserUtil; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.tag.Tag; /** * Jackson-friendly version of {@link Tag}. @@ -39,10 +41,9 @@ public String getTagName() { * @throws IllegalValueException if there were any data constraints violated in the adapted tag. */ public Tag toModelType() throws IllegalValueException { - if (!Tag.isValidTagName(tagName)) { - throw new IllegalValueException(Tag.MESSAGE_CONSTRAINTS); - } - return new Tag(tagName); + Tag tag; + tag = ParserUtil.parseTag(tagName); + return tag; } } diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/hirehive/address/storage/JsonAddressBookStorage.java similarity index 86% rename from src/main/java/seedu/address/storage/JsonAddressBookStorage.java rename to src/main/java/hirehive/address/storage/JsonAddressBookStorage.java index 41e06f264e1..323299c431c 100644 --- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java +++ b/src/main/java/hirehive/address/storage/JsonAddressBookStorage.java @@ -1,4 +1,4 @@ -package seedu.address.storage; +package hirehive.address.storage; import static java.util.Objects.requireNonNull; @@ -7,12 +7,12 @@ import java.util.Optional; import java.util.logging.Logger; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataLoadingException; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.FileUtil; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyAddressBook; +import hirehive.address.commons.core.LogsCenter; +import hirehive.address.commons.exceptions.DataLoadingException; +import hirehive.address.commons.exceptions.IllegalValueException; +import hirehive.address.commons.util.FileUtil; +import hirehive.address.commons.util.JsonUtil; +import hirehive.address.model.ReadOnlyAddressBook; /** * A class to access AddressBook data stored as a json file on the hard disk. diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/hirehive/address/storage/JsonSerializableAddressBook.java similarity index 88% rename from src/main/java/seedu/address/storage/JsonSerializableAddressBook.java rename to src/main/java/hirehive/address/storage/JsonSerializableAddressBook.java index 5efd834091d..d07ff363354 100644 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ b/src/main/java/hirehive/address/storage/JsonSerializableAddressBook.java @@ -1,4 +1,4 @@ -package seedu.address.storage; +package hirehive.address.storage; import java.util.ArrayList; import java.util.List; @@ -8,10 +8,10 @@ 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; +import hirehive.address.commons.exceptions.IllegalValueException; +import hirehive.address.model.AddressBook; +import hirehive.address.model.ReadOnlyAddressBook; +import hirehive.address.model.person.Person; /** * An Immutable AddressBook that is serializable to JSON format. diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/hirehive/address/storage/JsonUserPrefsStorage.java similarity index 82% rename from src/main/java/seedu/address/storage/JsonUserPrefsStorage.java rename to src/main/java/hirehive/address/storage/JsonUserPrefsStorage.java index 48a9754807d..fdfa50d3b49 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java +++ b/src/main/java/hirehive/address/storage/JsonUserPrefsStorage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package hirehive.address.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataLoadingException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import hirehive.address.commons.exceptions.DataLoadingException; +import hirehive.address.commons.util.JsonUtil; +import hirehive.address.model.ReadOnlyUserPrefs; +import hirehive.address.model.UserPrefs; /** * A class to access UserPrefs stored in the hard disk as a json file diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/hirehive/address/storage/Storage.java similarity index 71% rename from src/main/java/seedu/address/storage/Storage.java rename to src/main/java/hirehive/address/storage/Storage.java index 9fba0c7a1d6..d5680e133b9 100644 --- a/src/main/java/seedu/address/storage/Storage.java +++ b/src/main/java/hirehive/address/storage/Storage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package hirehive.address.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataLoadingException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import hirehive.address.commons.exceptions.DataLoadingException; +import hirehive.address.model.ReadOnlyAddressBook; +import hirehive.address.model.ReadOnlyUserPrefs; +import hirehive.address.model.UserPrefs; /** * API of the Storage component diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/hirehive/address/storage/StorageManager.java similarity index 89% rename from src/main/java/seedu/address/storage/StorageManager.java rename to src/main/java/hirehive/address/storage/StorageManager.java index 8b84a9024d5..0147021ddd6 100644 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ b/src/main/java/hirehive/address/storage/StorageManager.java @@ -1,15 +1,15 @@ -package seedu.address.storage; +package hirehive.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.DataLoadingException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import hirehive.address.commons.core.LogsCenter; +import hirehive.address.commons.exceptions.DataLoadingException; +import hirehive.address.model.ReadOnlyAddressBook; +import hirehive.address.model.ReadOnlyUserPrefs; +import hirehive.address.model.UserPrefs; /** * Manages storage of AddressBook data in local storage. diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/hirehive/address/storage/UserPrefsStorage.java similarity index 69% rename from src/main/java/seedu/address/storage/UserPrefsStorage.java rename to src/main/java/hirehive/address/storage/UserPrefsStorage.java index e94ca422ea8..e82457389d2 100644 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ b/src/main/java/hirehive/address/storage/UserPrefsStorage.java @@ -1,15 +1,15 @@ -package seedu.address.storage; +package hirehive.address.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataLoadingException; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import hirehive.address.commons.exceptions.DataLoadingException; +import hirehive.address.model.ReadOnlyUserPrefs; +import hirehive.address.model.UserPrefs; /** - * Represents a storage for {@link seedu.address.model.UserPrefs}. + * Represents a storage for {@link UserPrefs}. */ public interface UserPrefsStorage { @@ -27,7 +27,7 @@ public interface UserPrefsStorage { Optional readUserPrefs() throws DataLoadingException; /** - * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage. + * Saves the given {@link 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/hirehive/address/ui/CommandBox.java similarity index 88% rename from src/main/java/seedu/address/ui/CommandBox.java rename to src/main/java/hirehive/address/ui/CommandBox.java index 9e75478664b..7c9bef7a5f3 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/hirehive/address/ui/CommandBox.java @@ -1,12 +1,13 @@ -package seedu.address.ui; +package hirehive.address.ui; +import hirehive.address.logic.Logic; +import hirehive.address.logic.commands.CommandResult; +import hirehive.address.logic.commands.exceptions.CommandException; +import hirehive.address.logic.parser.exceptions.ParseException; 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; /** * The UI component that is responsible for receiving user command inputs. @@ -77,7 +78,7 @@ public interface CommandExecutor { /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see 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/hirehive/address/ui/HelpWindow.java similarity index 92% rename from src/main/java/seedu/address/ui/HelpWindow.java rename to src/main/java/hirehive/address/ui/HelpWindow.java index 3f16b2fcf26..4031277c4a0 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/hirehive/address/ui/HelpWindow.java @@ -1,21 +1,21 @@ -package seedu.address.ui; +package hirehive.address.ui; import java.util.logging.Logger; +import hirehive.address.commons.core.LogsCenter; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.stage.Stage; -import seedu.address.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://ay2425s2-cs2103t-f13-3.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/hirehive/address/ui/MainWindow.java similarity index 67% rename from src/main/java/seedu/address/ui/MainWindow.java rename to src/main/java/hirehive/address/ui/MainWindow.java index 79e74ef37c0..db26603670b 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/hirehive/address/ui/MainWindow.java @@ -1,21 +1,32 @@ -package seedu.address.ui; +package hirehive.address.ui; + +import static hirehive.address.logic.Messages.MESSAGE_DATA_SAVED; +import static hirehive.address.logic.Messages.MESSAGE_EMPTY_ADDRESS_BOOK; +import static hirehive.address.logic.Messages.MESSAGE_LOAD_SUCCESS; +import static hirehive.address.logic.Messages.MESSAGE_SAMPLE_ADDRESS_BOOK; import java.util.logging.Logger; +import hirehive.address.commons.core.GuiSettings; +import hirehive.address.commons.core.LogsCenter; +import hirehive.address.logic.Logic; +import hirehive.address.logic.commands.CommandResult; +import hirehive.address.logic.commands.ExitCommand; +import hirehive.address.logic.commands.ListCommand; +import hirehive.address.logic.commands.exceptions.CommandException; +import hirehive.address.logic.parser.exceptions.ParseException; +import hirehive.address.model.AddressBook; +import hirehive.address.model.ReadOnlyAddressBook; +import hirehive.address.model.util.SampleDataUtil; import javafx.event.ActionEvent; import javafx.fxml.FXML; +import javafx.scene.control.Label; import javafx.scene.control.MenuItem; import javafx.scene.control.TextInputControl; import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; import javafx.scene.layout.StackPane; 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; /** * The Main Window. Provides the basic application layout containing @@ -34,6 +45,7 @@ public class MainWindow extends UiPart { private PersonListPanel personListPanel; private ResultDisplay resultDisplay; private HelpWindow helpWindow; + private NoteWindow noteWindow; @FXML private StackPane commandBoxPlaceholder; @@ -50,6 +62,9 @@ public class MainWindow extends UiPart { @FXML private StackPane statusbarPlaceholder; + @FXML + private Label contactCountLabel; + /** * Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}. */ @@ -66,6 +81,7 @@ public MainWindow(Stage primaryStage, Logic logic) { setAccelerators(); helpWindow = new HelpWindow(); + noteWindow = new NoteWindow(); } public Stage getPrimaryStage() { @@ -113,9 +129,13 @@ void fillInnerParts() { personListPanel = new PersonListPanel(logic.getFilteredPersonList()); personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + updateContactCount(); + resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); + loadAddressBookMessage(); + StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); @@ -147,6 +167,18 @@ public void handleHelp() { } } + /** + * Opens the note window or focuses on it if it's already opened. + */ + @FXML + public void handleNote() { + if (!noteWindow.isShowing()) { + noteWindow.show(); + } else { + noteWindow.focus(); + } + } + void show() { primaryStage.show(); } @@ -159,6 +191,7 @@ private void handleExit() { GuiSettings guiSettings = new GuiSettings(primaryStage.getWidth(), primaryStage.getHeight(), (int) primaryStage.getX(), (int) primaryStage.getY()); logic.setGuiSettings(guiSettings); + noteWindow.hide(); helpWindow.hide(); primaryStage.hide(); } @@ -170,13 +203,21 @@ public PersonListPanel getPersonListPanel() { /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see 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()); + String userFeedback = commandResult.getFeedbackToUser(); + // if command has edited the applicant book in some way + if (commandResult.isChange()) { + userFeedback += MESSAGE_DATA_SAVED; + } + resultDisplay.setFeedbackToUser(userFeedback); + noteWindow.setNote(logic); + + updateContactCount(); if (commandResult.isShowHelp()) { handleHelp(); @@ -186,11 +227,38 @@ private CommandResult executeCommand(String commandText) throws CommandException handleExit(); } + if (commandResult.isShowNote()) { + handleNote(); + } + return commandResult; } catch (CommandException | ParseException e) { logger.info("An error occurred while executing command: " + commandText); resultDisplay.setFeedbackToUser(e.getMessage()); + updateContactCount(); throw e; } } + + /** + * Displays status of addressBook in the results box in the GUI upon loading. + */ + private void loadAddressBookMessage() { + ReadOnlyAddressBook currentAddressBook = logic.getAddressBook(); + // Data file could not be read, loads empty AddressBook instead + if (currentAddressBook.equals(new AddressBook())) { + resultDisplay.setFeedbackToUser(MESSAGE_EMPTY_ADDRESS_BOOK); + // Data file does not exist, load sample AddressBook instead + } else if (currentAddressBook.equals(SampleDataUtil.getSampleAddressBook())) { + resultDisplay.setFeedbackToUser(MESSAGE_SAMPLE_ADDRESS_BOOK); + } else { + // Data file loaded successfully + resultDisplay.setFeedbackToUser(MESSAGE_LOAD_SUCCESS); + } + } + + private void updateContactCount() { + int count = logic.getFilteredPersonListSize(); + contactCountLabel.setText("Total contacts displayed: " + count); + } } diff --git a/src/main/java/hirehive/address/ui/NoteWindow.java b/src/main/java/hirehive/address/ui/NoteWindow.java new file mode 100644 index 00000000000..e7caf9d76f6 --- /dev/null +++ b/src/main/java/hirehive/address/ui/NoteWindow.java @@ -0,0 +1,108 @@ +package hirehive.address.ui; + +import java.util.logging.Logger; + +import hirehive.address.commons.core.LogsCenter; +import hirehive.address.logic.Logic; +import hirehive.address.model.person.Note; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.TextArea; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; +import javafx.stage.Stage; + +/** + * Controller for the note page. + */ +public class NoteWindow extends UiPart { + + public static final String EMPTY_NOTE = "Currently empty..."; + + private static final Logger logger = LogsCenter.getLogger(NoteWindow.class); + private static final String FXML = "NoteWindow.fxml"; + + @FXML + private TextArea note; + + @FXML + private Button save; + + /** + * Creates a new NoteWindow. + * @param root Stage to use as the root of the NoteWindow. + */ + public NoteWindow(Stage root) { + super(FXML, root); + note.setText(EMPTY_NOTE); + } + + /** + * Creates a new NoteWindow. + */ + public NoteWindow() { + this(new Stage()); + } + + /** + * Sets the note in the NoteWindow. + * @param logic from the MainWindow. + */ + public void setNote(Logic logic) { + Note newNote = logic.getPersonNote(); + if (newNote.isEmpty()) { + note.setText(EMPTY_NOTE); + note.setStyle("-fx-text-fill: yellow;"); + } else { + note.setText(newNote.value); + note.setStyle("-fx-text-fill: white;"); + } + } + + /** + * Shows the NoteWindow. + */ + public void show() { + logger.fine("Showing note page."); + getRoot().show(); + getRoot().centerOnScreen(); + } + + /** + * Returns true if the note window is currently being shown. + */ + public boolean isShowing() { + return getRoot().isShowing(); + } + + /** + * Hides the note window. + */ + public void hide() { + note.setText(EMPTY_NOTE); + getRoot().hide(); + } + + /** + * Focuses on the note window. + */ + public void focus() { + getRoot().requestFocus(); + } + + /** + * Copies the contents of the note to the clipboard. + */ + @FXML + public void copyNote() { + final Clipboard clipboard = Clipboard.getSystemClipboard(); + final ClipboardContent noteText = new ClipboardContent(); + if (note.getStyle().equals("-fx-text-fill: white;")) { + noteText.putString(note.getText()); + } else { + noteText.putString(""); + } + clipboard.setContent(noteText); + } + +} diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/hirehive/address/ui/PersonCard.java similarity index 71% rename from src/main/java/seedu/address/ui/PersonCard.java rename to src/main/java/hirehive/address/ui/PersonCard.java index 094c42cda82..a8f8fa87609 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/hirehive/address/ui/PersonCard.java @@ -1,13 +1,13 @@ -package seedu.address.ui; +package hirehive.address.ui; import java.util.Comparator; +import hirehive.address.model.person.Person; import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; -import seedu.address.model.person.Person; /** * An UI component that displays information of a {@code Person}. @@ -39,7 +39,13 @@ public class PersonCard extends UiPart { @FXML private Label email; @FXML + private Label role; + @FXML private FlowPane tags; + @FXML + private Label date; + @FXML + private Label note; /** * Creates a {@code PersonCode} with the given {@code Person} and index to display. @@ -52,8 +58,11 @@ public PersonCard(Person person, int displayedIndex) { phone.setText(person.getPhone().value); address.setText(person.getAddress().value); email.setText(person.getEmail().value); - person.getTags().stream() - .sorted(Comparator.comparing(tag -> tag.tagName)) - .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + role.setText(person.getRole().toString()); + Label tag = new Label(person.getTag().getTagName()); + tag.getStyleClass().add(person.getTag().getTagName().toLowerCase()); + tags.getChildren().add(tag); + date.setText(person.getDate().toString().isBlank() ? "Date not set" : person.getDate().toString()); + note.setText("Note Contents: " + (person.getNote().isEmpty() ? "Empty" : "Not empty")); } } diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/hirehive/address/ui/PersonListPanel.java similarity index 91% rename from src/main/java/seedu/address/ui/PersonListPanel.java rename to src/main/java/hirehive/address/ui/PersonListPanel.java index f4c501a897b..085b8277876 100644 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ b/src/main/java/hirehive/address/ui/PersonListPanel.java @@ -1,14 +1,14 @@ -package seedu.address.ui; +package hirehive.address.ui; import java.util.logging.Logger; +import hirehive.address.commons.core.LogsCenter; +import hirehive.address.model.person.Person; 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. diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/hirehive/address/ui/ResultDisplay.java similarity index 95% rename from src/main/java/seedu/address/ui/ResultDisplay.java rename to src/main/java/hirehive/address/ui/ResultDisplay.java index 7d98e84eedf..4d6b6f613c6 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/hirehive/address/ui/ResultDisplay.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package hirehive.address.ui; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/hirehive/address/ui/StatusBarFooter.java similarity index 95% rename from src/main/java/seedu/address/ui/StatusBarFooter.java rename to src/main/java/hirehive/address/ui/StatusBarFooter.java index b577f829423..bb31a56b0fd 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/hirehive/address/ui/StatusBarFooter.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package hirehive.address.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/hirehive/address/ui/Ui.java similarity index 84% rename from src/main/java/seedu/address/ui/Ui.java rename to src/main/java/hirehive/address/ui/Ui.java index 17aa0b494fe..9d946e615d3 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/hirehive/address/ui/Ui.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package hirehive.address.ui; import javafx.stage.Stage; diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/hirehive/address/ui/UiManager.java similarity index 93% rename from src/main/java/seedu/address/ui/UiManager.java rename to src/main/java/hirehive/address/ui/UiManager.java index fdf024138bc..da1c6cdc098 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/hirehive/address/ui/UiManager.java @@ -1,16 +1,16 @@ -package seedu.address.ui; +package hirehive.address.ui; import java.util.logging.Logger; +import hirehive.address.MainApp; +import hirehive.address.commons.core.LogsCenter; +import hirehive.address.commons.util.StringUtil; +import hirehive.address.logic.Logic; import javafx.application.Platform; import javafx.scene.control.Alert; 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; /** * The manager of the UI component. diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/hirehive/address/ui/UiPart.java similarity index 97% rename from src/main/java/seedu/address/ui/UiPart.java rename to src/main/java/hirehive/address/ui/UiPart.java index fc820e01a9c..76fc15a768d 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/hirehive/address/ui/UiPart.java @@ -1,12 +1,12 @@ -package seedu.address.ui; +package hirehive.address.ui; import static java.util.Objects.requireNonNull; import java.io.IOException; import java.net.URL; +import hirehive.address.MainApp; import javafx.fxml.FXMLLoader; -import seedu.address.MainApp; /** * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java deleted file mode 100644 index ecd32c31b53..00000000000 --- a/src/main/java/seedu/address/logic/Messages.java +++ /dev/null @@ -1,51 +0,0 @@ -package seedu.address.logic; - -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import seedu.address.logic.parser.Prefix; -import seedu.address.model.person.Person; - -/** - * Container for user visible messages. - */ -public class Messages { - - public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; - public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; - public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; - public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; - public static final String MESSAGE_DUPLICATE_FIELDS = - "Multiple values specified for the following single-valued field(s): "; - - /** - * Returns an error message indicating the duplicate prefixes. - */ - public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePrefixes) { - assert duplicatePrefixes.length > 0; - - Set duplicateFields = - Stream.of(duplicatePrefixes).map(Prefix::toString).collect(Collectors.toSet()); - - return MESSAGE_DUPLICATE_FIELDS + String.join(" ", duplicateFields); - } - - /** - * Formats the {@code person} for display to the user. - */ - public static String format(Person person) { - final StringBuilder builder = new StringBuilder(); - builder.append(person.getName()) - .append("; Phone: ") - .append(person.getPhone()) - .append("; Email: ") - .append(person.getEmail()) - .append("; Address: ") - .append(person.getAddress()) - .append("; Tags: "); - person.getTags().forEach(builder::append); - return builder.toString(); - } - -} diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java deleted file mode 100644 index 1135ac19b74..00000000000 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ /dev/null @@ -1,69 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.ToStringBuilder; -import seedu.address.logic.Messages; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.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, Messages.format(personToDelete))); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof DeleteCommand)) { - return false; - } - - DeleteCommand otherDeleteCommand = (DeleteCommand) other; - return targetIndex.equals(otherDeleteCommand.targetIndex); - } - - @Override - public String toString() { - return new ToStringBuilder(this) - .add("targetIndex", targetIndex) - .toString(); - } -} 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 3527fe76a3e..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.logic.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 46b3309a78b..00000000000 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ /dev/null @@ -1,85 +0,0 @@ -package seedu.address.logic.parser; - -import static java.util.Objects.requireNonNull; -import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.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); - } - - argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS); - - 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/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java deleted file mode 100644 index 62d19be2977..00000000000 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ /dev/null @@ -1,44 +0,0 @@ -package seedu.address.model.person; - -import java.util.List; -import java.util.function.Predicate; - -import seedu.address.commons.util.StringUtil; -import seedu.address.commons.util.ToStringBuilder; - -/** - * 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) { - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof NameContainsKeywordsPredicate)) { - return false; - } - - NameContainsKeywordsPredicate otherNameContainsKeywordsPredicate = (NameContainsKeywordsPredicate) other; - return keywords.equals(otherNameContainsKeywordsPredicate.keywords); - } - - @Override - public String toString() { - return new ToStringBuilder(this).add("keywords", keywords).toString(); - } -} 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 abe8c46b535..00000000000 --- a/src/main/java/seedu/address/model/person/Person.java +++ /dev/null @@ -1,117 +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.commons.util.ToStringBuilder; -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; - } - - // instanceof handles nulls - if (!(other instanceof Person)) { - return false; - } - - Person otherPerson = (Person) other; - return name.equals(otherPerson.name) - && phone.equals(otherPerson.phone) - && email.equals(otherPerson.email) - && address.equals(otherPerson.address) - && tags.equals(otherPerson.tags); - } - - @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() { - return new ToStringBuilder(this) - .add("name", name) - .add("phone", phone) - .add("email", email) - .add("address", address) - .add("tags", tags) - .toString(); - } - -} diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java deleted file mode 100644 index f1a0d4e233b..00000000000 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ /dev/null @@ -1,62 +0,0 @@ -package seedu.address.model.tag; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Tag in the address book. - * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} - */ -public class Tag { - - public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric"; - public static final String VALIDATION_REGEX = "\\p{Alnum}+"; - - public final String tagName; - - /** - * Constructs a {@code Tag}. - * - * @param tagName A valid tag name. - */ - public Tag(String tagName) { - requireNonNull(tagName); - checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS); - this.tagName = tagName; - } - - /** - * Returns true if a given string is a valid tag name. - */ - public static boolean isValidTagName(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof Tag)) { - return false; - } - - Tag otherTag = (Tag) other; - return tagName.equals(otherTag.tagName); - } - - @Override - public int hashCode() { - return tagName.hashCode(); - } - - /** - * Format state as text for viewing. - */ - public String toString() { - return '[' + tagName + ']'; - } - -} 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/resources/images/note_icon.png b/src/main/resources/images/note_icon.png new file mode 100644 index 00000000000..1cf8ba73635 Binary files /dev/null and b/src/main/resources/images/note_icon.png differ diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 36e6b001cd8..5bd93aadc1f 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -146,6 +146,10 @@ -fx-background-color: derive(#1d1d1d, 30%); } +.status-bar-label { + -fx-text-fill: #FFFFFF; +} + .result-display { -fx-background-color: transparent; -fx-font-family: "Segoe UI Light"; @@ -344,9 +348,28 @@ #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; } + +#tags .rejected { + -fx-background-color: #913F51; +} + +#tags .applicant { + -fx-background-color: #3e7b91; +} + +#tags .candidate { + -fx-background-color: #3C8F76; +} + +#tags .interviewee { + -fx-background-color: #4B8F3A; +} + +#tags .offered { + -fx-background-color: #918C3F; +} diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index 7778f666a0a..348755821f2 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -3,6 +3,7 @@ + @@ -12,7 +13,7 @@ + title="HireHive" minWidth="450" minHeight="600" onCloseRequest="#handleExit"> @@ -46,6 +47,13 @@ + + + + + + diff --git a/src/main/resources/view/NoteWindow.css b/src/main/resources/view/NoteWindow.css new file mode 100644 index 00000000000..41af2d974e6 --- /dev/null +++ b/src/main/resources/view/NoteWindow.css @@ -0,0 +1,28 @@ +#note { + -fx-background-color: derive(#1d1d1d, 20%); + -fx-border-color: white; + -fx-text-fill: white; +} + +#note .content { + -fx-background-color: derive(#1d1d1d, 20%); +} + +#noteContainer { + -fx-background-color: derive(#1d1d1d, 20%); +} + +#copy { + -fx-text-fill: white; + -fx-background-color: dimgray; + +} + +#copy:hover { + -fx-background-color: gray; +} + +#copy:armed { + -fx-background-color: green; + +} diff --git a/src/main/resources/view/NoteWindow.fxml b/src/main/resources/view/NoteWindow.fxml new file mode 100644 index 00000000000..a03db00e77b --- /dev/null +++ b/src/main/resources/view/NoteWindow.fxml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + +