This project is based on the AddressBook-Level3 project created by the SE-EDU initiative.
Refer to the guide Setting up and getting started.
  The Architecture Diagram given above explains the high-level design of the App.
Given below is a quick overview of main components and how they interact with each other.
Main components of the architecture
Main (consisting of classes Main and MainApp) is in charge of the app launch and shut down.
The bulk of the app's work is done by the following four components:
UI: The UI of the App.Logic: The command executor.Model: Holds the data of the App in memory.Storage: Reads data from, and writes data to, the hard disk.Commons represents a collection of classes used by multiple other components.
How the architecture components interact with each other
The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command delete 1.
  Each of the four main components (also shown in the diagram above),
interface with the same name as the Component.{Component Name}Manager class (which follows the corresponding API interface mentioned in the previous point.For example, the Logic component defines its API in the Logic.java interface and implements its functionality using the LogicManager.java class which follows the Logic interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
  The sections below give more details of each component.
The API of this component is specified in Ui.java
  The UI consists of a MainWindow that is made up of parts e.g.CommandBox, ResultDisplay, PersonListPanel, StatusBarFooter, InterviewListPanel 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 is specified in MainWindow.fxml
The UI component,
Logic component.Model data so that the UI can be updated with the modified data.Logic component, because the UI relies on the Logic to execute commands.Model component, as it displays Person and Interview objects residing in the Model.API : 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.
  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.
How the Logic component works:
Logic is called upon to execute a command, it is passed to an AddressBookParser object which in turn creates a parser that matches the command (e.g., DeleteCommandParser) and uses it to parse the command.Command object (more precisely, an object of one of its subclasses e.g., DeleteCommand) which is executed by the LogicManager.Model when it is executed (e.g. to delete a person).Model) to achieve.CommandResult object which is returned back from Logic.Here are the other classes in Logic (omitted from the class diagram above) that are used for parsing a user command:
  How the parsing works:
AddressBookParser class creates an XYZCommandParser (XYZ is a placeholder for the specific command name e.g., AddCommandParser) which uses the other classes shown above to parse the user command and create a XYZCommand object (e.g., AddCommand) which the AddressBookParser returns back as a Command object.XYZCommandParser classes (e.g., AddCommandParser, DeleteCommandParser, ...) inherit from the Parser interface so that they can be treated similarly where possible e.g, during testing.API : Model.java
  The Model component,
Person objects (which are contained in a UniquePersonList object).Person objects (e.g., results of a search query) as a separate filtered list which is exposed to outsiders as an unmodifiable ObservableList<Person> that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.UserPref object that represents the user’s preferences. This is exposed to the outside as a ReadOnlyUserPref objects.Interview objects (which are contained in a UniqueInterviewList object).Interview objects (e.g., results of a search query) as a separate filtered list which is exposed to outsiders as an unmodifiable ObservableList<Interview> that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.Model represents data entities of the domain, they should make sense on their own without depending on other components)Note: An alternative (arguably, a more OOP) model is given below. It has a Tag list in the AddressBook, which Person references. This allows AddressBook to only require one Tag object per unique tag, instead of each Person needing their own Tag objects.
 API : Storage.java
  The Storage component,
AddressBookStorage and UserPrefStorage, which means it can be treated as either one (if only the functionality of only one is needed).Model component (because the Storage component's job is to save/retrieve objects that belong to the Model)Classes used by multiple components are in the seedu.addressbook.commons package.
This section describes some noteworthy details on how certain features are implemented.
An interview contains the following fields: Date, Start time, End time, Description, and two Person instances representing the applicant and interviewer.
The find mechanism is facilitated by 3 classes FindEmailCommand, FindNameCommand and FindPhoneCommand. They all extend FindCommand with their corresponding COMMAND_WORD : find_email, find_name and find_phone respectively, as well as a corresponding predicate variable of type EmailContainsKeywordsPredicate, NameContainsKeywordsPredicate and PhoneContainsKeywordsPredicate respectively.
EmailContainsKeywordsPredicate, NameContainsKeywordsPredicate and PhoneContainsKeywordsPredicate extend Predicate from the java.util.function package and override the test function to match their respective criteria of matching Email, Name and Phone values respectively.
These Predicate objects allow for matching of multiple substrings, facilitating searching for multiple persons in the application simultaneously. This is done by providing multiple keyword arguments after the find_[email/name/phone] command word. However, this only applies to keywords for the same criteria.
Example: Keyword arguments after find_name will be matched only to the Name values of persons in application data, and not theie Email or Phone values.
FindEmailCommand#execute() — Searches for persons based on the specified email keywords.FindNameCommand#execute() — Searches for persons based on the specified name keywords.FindPhoneCommand#execute() — Searches for persons based on the specified phone keywords.history.The above execute operations utilise ModelManager#updateFilteredPersonList() implemented from the Model interface to update the GUI to display the persons that match the criteria provided as arguments to the FindCommand variant.
The following class diagram summarizes the organisation of the FindCommand variant classes.
  Given below is an example usage scenario and how the find mechanism behaves at each step. All 3 variants behave in the same way, just with their keywords being of different types.
Step 1. The user launches the application which loads in data from the previous session. Current data in the application include 2 Applicant objects, one with Name = "Ryan" and the other with Name = "Wesley".
Step 2. The user executes find_name command to find a person with the name Ryan in the application. The find_name command calls FindNameCommandParser#parse(), creating a new FindNameCommand object initialised with a NameContainsKeywordsPredicate object that is created with an array of the keywords passed as arguments with the find_name command. When FindNameCommand#execute() is called, the list displayed on the GUI will be updated to show only the entry of Ryan:Applicant.
Note:
find_name command word and will result in an ParseException indicating invalid command format otherwise.list_persons to display the original list of all persons on the GUIfind_[email/name/phone] command
  Aspect: How find is implemented:
Alternative 1 (current choice): 3 separate commands for phone, email, and name.
Alternative 2: Command word remains as find and the first argument after will determine the criteria to search for: email, name or phone.
The applicant status mechanism is facilitated by AddApplicantStatusCommand. It extends Command with its own status field, stored internally as ApplicantStatus.
ApplicantStatus encapsulates statuses (enumerated in ApplicantState) in a value field.
AddApplicantStatusCommand implements the following operations:
AddApplicantStatusCommand#execute() — Adds on the encapsulated currentStatus to the applicant in question.ApplicantStatus also enables the following functionality in Applicant:
Applicant#updateCurrentStatusToReflectScheduledInterview() — Updates the currentStatus of the applicant to "pending interview".Applicant#revertCurrentStatus() — Reverts the currentStatus of the applicant to "resume review".Applicant#getCurrentStatus() — Returns the stringified version of the encapsulated currentStatus which is simply its value"Unlike for applicant statuses, there is no AddInterviewerStatusCommand available for users. interviewer statuses are managed only internally between Interview, InterviewerStatus and Interviewer.
Another important difference is that while an Applicant contains at most 1 currentStatus, an Interviewer contains a variable-size list of upcomingInterviews containing objects of type InterviewerStatus in the form of "interview with [applicant name]".
The interviewer status mechanism is facilitated by InterviewerStatus. It extends Status and
encapsulates statuses (enumerated in InterviewerState) in a value field represented as a String.
InterviewerStatus enables the following functionality in Interviewer:
Interviewer#updateCurrentStatusToReflectScheduledInterview() — Appends a new "interview with..." status to the list of upcomingInterviews of the interviewer.Interviewer#updateCurrentStatusToReflectDeletedInterview()() — Removed the specific "interview with..." status of from the list of upcomingInterviews of the interviewer.Interviewer#getCurrentStatus() — Returns the stringified version of the encapsulated upcomingInterviews list by retrieving the stringified version of each of the InterviewStatus in the upcomingInterviews list and separating them with a newline.
InterviewerStatus of "free" inside upcomingInterviews, the list is left empty and getCurrentStatus just returns the String "free" instead.The following class diagram shows the overall structure of AddApplicantStatusCommand, ApplicantStatus, InterviewerStatus:
  Given below is an example usage scenario and how the applicant/interviewer status mechanism behaves at each step.
Step 1. The user launches the application for the first time and executes add_applicant n/Yash p/98362254 e/yashwit@u.nus.edu. An applicant Yash is initialised with default currentStatus "resume review".
Step 2. The user executes add_interviewer n/Ryan p/12345678 e/ryan@u.nus.edu. An interviewer Ryan is initialised with default upcomingInterviews .
Step 3. The user executes add_interview....a/98362254 i/12345678 to create an interview between Yash and Ryan. The add_interview command makes a call to updateCurrentStatusToReflectScheduledInterview in Yash, which updates Yash's currentStatus to "pending interview". Similarly, a call is made to updateCurrentStatusToReflectInterview in Ryan and Ryan's upcomingInterviews is appended with "interview with Yash". The updateCurrentStatusToReflectScheduledInterview methods in Ryan and Yash in-turn call the setPerson list of the current Model for the status change to be reflected immediately.
Note: If the add_interview command fails its execution, it will not call updateCurrentStatusToReflectScheduledInterview, so the address book state will not be modified.
Step 4. The user now decides that she wants to edit Yash's status to "completed interview" manually, and executes the applicant_status 98362254 s/completed interview command. The applicant_status command will call AddApplicantStatusCommandParser#parse(), which will verify the validity of the status through ApplicantStatus#isValidStatus() through ParserUtil before creating an ApplicantStatus and then an AddApplicantStatusCommand.
Note: If the status passed by the user matches with none of the statuses enumerated in ApplicantState, a new ApplicantStatus is not created and consequently neither is an AddApplicantStatusCommand.
The following sequence diagram illustrates step 4:
  Aspect: Customisability of statuses for users:
Alternative 1 (current choice): Users can only set those applicant statuses enumerated in ApplicantState.
Alternative 2: Custom statuses.
Tag architecture to implement statuses since they are very similar and there is more leeway in setting tags.The add interview mechanism is facilitated by AddInterviewCommand. They all extend the Command with fields called description, applicant phone number,
interviewer phone number, date of interview, startTime as well as endTime. An Interview is created then added to the list.
AddInterviewCommand implements the following operations:
AddInterviewCommand#execute()  —  Adds the encapsulated Interview to the list.The above execute operation utilises ModelManager#updateFilteredPersonList() implemented from the Model interface to obtain the list of applicant and interviewer phone numbers.
This is followed by checking the validity of the phone numbers before creating an Interview object to be added into the interview list. The operation execute then utilises
ModelManager#addInterview() implemented from the Model to add the Interview to the list. The operation execute also utilises ModelManager#sortInterview() to the interview objects by date, startTime and endTime.
The following class diagram summarizes the organisation of the AddInterviewCommand:
  Given below is an example usage scenario and how the mechanism behaves at each step.
Step 1. The user launches the application for the first time and executes add_applicant n/Wesley p/81159858 e/ywesley16@gmail.com. An applicant Wesley is initialised.
Step 2. The user executes add_interviewer n/Yash p/98362254 e/yashwit@u.nus.edu. An interviewer Yash is initialised.
Step 3. The user executes add_interview desc/technical interview date/2024-03-28 st/10:00 et/11:00 a/81159858 i/98362254. The add_interview command calls the AddInterviewCommandParser#parse(),
creating a new AddInterviewCommand object initialised with the respective fields. When the AddInterviewCommand#excute() is called, a check is conducted to determine whether the Interview is already scheduled.
This is then followed by creating Interview object and adding it into the list. The list of interviews will then be sorted. The GUI will display the interviews under the interview column.
Note:
ParseException indicating invalid command format otherwise.CommandException indicating a duplicate interview has been entered.CommandException indicating which phone number is incorrect.The following sequence diagram shows demonstrates step 3:
  Aspect: How interviews are added:
Alternative 1(current choice): Creates the Interview inside AddInterviewCommand.
Interview.Alternative 2: Interview is created under AddInterviewCommandParser.
The interview and person saving mechanism in the application is powered through the coordination of seven key components:
JsonSerializableAddressBook, JsonAdaptedStorageBook JsonAdaptedPerson, JsonAdaptedTag, JsonAdaptedInterview, LogicManager, and StorageManager.
The LogicManager triggers data persistence post-command execution by invoking StorageManager's saveAddressBook function
which in turn triggers JsonAddressBookStorage's saveAddressBook command.
This process generates a new JsonSerializableAddressBook object, encapsulating lists for both persons and
interviews.
The Model class harbors an addressBook with distinct
lists (UniquePersonList and UniqueInterviewList) for storing Person
and Interview entities. During the serialization phase, these entities are transformed into
JSON format, utilizing JsonAdaptedPerson and JsonAdaptedInterview for accurate mapping.
Each JsonAdaptedPerson retains Person attributes as @JsonProperty and encapsulates tags
as JsonAdaptedTag collections. Likewise, JsonAdaptedInterview conserves Interview attributes,
additionally incorporating applicant and interviewer information as JsonAdaptedPerson instances.
The following sequence diagram illustrates a simplified version of the above process.
  Aspect: How interviews/persons are stored:
Converting person and interview data to JSON format facilitates easy data storage, retrieval, and manipulation. JSON, being lightweight and text-based, is highly compatible across different systems, making data sharing and application scaling more efficient. It also supports hierarchical data structures, which is useful for representing complex data relationships. JSON's human-readable format simplifies debugging and development processes, enhancing overall productivity.
Alternative 1(current choice): Save interviews/persons in JSON format.
Pros: Human-readable format simplifies debugging and development processes, compatible with web APIs and databases.
Cons: Harder to implement for a beginner developer.
Alternative 2: Save interview/person directly as strings.
The listing mechanism is facilitated by ListCommand and ListInterviewsCommand. They all extend the Command class. ListCommand is responsible for listing persons while ListInterviewsCommand is for listing interviews.
The command words for ListCommand and ListInterviewsCommand are list_persons and list_interviews respectively.
Both ListCommand and ListInterviewsCommand implements the following operations:
ListCommand / ListInterviewsCommand #execute()  -  Updates the list in Model with the original list.The above execute operation utilises ModelManager#updateFilteredPersonList() or ModelManager#updateFilteredInterviewList() respectively implemented from the Model interface, with the PREDICATE_SHOW_ALL_PERSONS Predicate, to update the list in Model with the full list.
This change is then reflected in the UI list of persons / Interviews.
The following class diagram summarizes the organisation of the two different list commands:
  Given below is an example usage scenario for interviews and how the mechanism behaves at each step.
Step 1. The user launches the application with existing interviewers, applicants, and interviews. The interviews' date are all different.
Step 2. The user executes the command filter_interviews_by_date 2024-02-03. Only interviews on the date 2024-02-03 will show up on the list of interviews on right side of the application.
Step 3. Now the user wants to see the original list of interviews in step 1 (eg. All interviews, regardless of date). The user can enter the command list_interviews. Now the list on the right side of the application will show the full list of interviews as in Step 1.
The scenario for persons is also similar to interviews.
Note:
list_persons and list_interviews commands have no arguments.The deleting mechanism for persons and interview are facilitated by DeleteCommand and DeleteInterviewCommand. They both extends from Command class. DeleteCommand is responsible for deleting a person while DeleteInterviewCommand is for deleting an interview.
The command words are delete_person and delete_interview respectively. DeleteCommand takes in a phone number to identify the person to delete, while DeleteInterviewsCommand takes in the index of the interview to delete.
Both DeleteCommand and DeleteInterviewsCommand implements the following operations:
DeleteCommand / DeleteInterviewsCommand #execute()  -  Removes the corresponding person or interview from the list in Model.The above execute operation utilises ModelManager#deletePerson() or ModelManager#deleteInterview() respectively implemented from the Model interface,to remove the corresponding person or interview from the list in Model.
This change is then reflected in the UI list of persons / Interviews.
The following class diagram summarizes the organisation of the two different delete commands:
  Note:
CommandException indicating an out of bounds or invalid phone number error.ParseException indicating invalid command format.Target user profile: 
Hiring manager who:
Value proposition: 
Free alternative for tracking interview datetimes, applicant contacts and their application statuses.
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 Tether | 
* * * |  user | add a new person (applicant/interviewer) | |
* * * |  user | delete a person (applicant/interviewer) | remove person entries that I no longer need | 
* * * |  user | add a new interview | |
* * * |  user | delete an interviewer | remove interview entries that I no longer need | 
* * |  user with many persons in Tether | find a person by name/email | locate details of a person without having to go through the entire list | 
* * |  user with many interviews in Tether | filter interviews by date | locate details of interviews without having to go through the entire list | 
* * |  user with many applicants of varying application status in Tether | record applicants' statuses | identify applicant's application progress | 
* * |  user with many interviewers of varying availabilities in the company | have a record of interviewer's scheduled interviews | identify which interviewers are occupied or available for interviews | 
* * |  user with many persons of varying application status in Tether | filter persons by status | contact all persons within a specific status group if necessary | 
*  |  user collaborating with other Tether users | share an applicant's details | update other hiring managers on applicant details | 
*  |  user who does not want to clutter local hard drive with files | store applicant's resume | view applicant's resume in Tether | 
* |  user with multiple ongoing interviews and many applicants/interviewers to track | view overall statistics of applicants/interviewers/interviews | keep abreast of the overall situation at all times | 
{More to be added}
(For all use cases below, the System is Tether and the Actor is the Hiring Manager, unless specified otherwise)
Use case: UC01 - List persons
MSS
User requests to list persons
System displays list of persons
Use case ends.
Use case: UC02 - Add a person with name, email and phone number
MSS
User requests to list persons (UC01)
User requests to add a new person to the list
System adds the person and updates the displayed list
Use case ends.
Extensions
2a. Any of the given name, email, phone number are invalid.
2a1. System shows an error message.
Use case resumes at step 1.
Use case: UC03 - Delete a person by phone number
MSS
User requests to list persons (UC01)
User requests to delete a specific person in the list
System deletes the person
Use case ends.
Extensions
2a. The given phone number is invalid or doesn't exist.
2a1. System shows an error message.
Use case resumes at step 1.
Use case: UC04 - Find a person by name/email/phone number
MSS
User requests to list persons (UC01)
User requests to find a specific person in the list by their name/email/phone number
System updates the list to only display the requested person
Use case ends.
Extensions
2a. The given name/email/phone number is invalid.
2a1. Tether shows an error message.
Use case resumes at step 1.
Use case: UC05 - Update an applicant's status
Precondition: There is at least 1 applicant in the system.
MSS
User requests to list persons (UC01).
User requests to update a specific applicant's status
System changes the applicant's status
Use case ends.
Extensions
2a. The given applicant phone number is invalid or doesn't exist.
2a1. System shows an error message.
Use case resumes at step 1.
2b. The given status is an invalid applicant status.
2a1. System shows an error message.
Use case resumes at step 1.
Use case: UC06 - Filter by status
Precondition: There is at least 1 applicant or interviewer in the system.
MSS
Extensions
2a. The given status is neither a valid applicant nor interviewer status.
2a1. System shows an error message.
Use case resumes at step 1.
Use case: UC07 - List interviews
MSS
User requests to list interviews
System shows a list of interviews
Use case ends.
Use case: UC08 - Add an interview
MSS
User requests to list interviews (UC07)
User requests to add a new interview to the list
System adds the interview and updates the displayed list
Use case ends.
Extensions
3a. Any of the given description, date, time, phone number are invalid.
3a1. System shows an error message.
Use case resumes at step 1.
Use case: UC09 - Delete an interview
MSS
User requests to list interviews (UC07)
User requests to delete a specific interview in the list
System deletes the interview
Use case ends.
Extensions
2a. The list is empty.
2a1. System shows an error message.
Use case resumes at step 1.
3a. The given index is invalid.
3a1. System shows an error message.
Use case resumes at step 1.
Use case: UC10 - Filtering interviews by date
MSS
User requests to list interviews (UC07)
User requests to filter interview by a specified date
System updates the list to only display interviews that match the specified date
Use case ends.
Extensions
2a. The given date is invalid.
2a1. Tether shows an error message.
Use case resumes at step 1.
2b. There are no interviews on the specified date.
2b1. Tether shows a no interviews found message.
Use case resumes at step 1.
Use case: UC11 - View overall statistics
MSS
User requests to list persons (UC01).
User requests to view overall statistics
System displays number of applicants (total, and by status), number of interviewers (total, and by status), and number of interviews
Use case ends.
{More to be added}
{More to be added}
Applicant or an InterviewerGiven below are instructions to test the app manually.
Note: These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.
Initial launch
Download tether.jar and copy into an empty folder
Double-click the jar file or run java -jar tether.jar.
Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
Saving window preferences
Resize the window to an optimum size. Move the window to a different location. Close the window.
Re-launch the app by double-clicking the jar file or running java -jar tether.jar.
Expected: The most recent window size and location is retained.
Closing the app using the exit command
Closing the app by clicking on the close button
Closing the app by clicking on the Exit button in the File tab
Click on the File tab in the top left corner of the app window.
Click on the Exit button that is displayed under the File tab.
Expected: The app window closes.
Deleting a person while all persons are being shown
Prerequisites: List all persons using the list_persons command. Multiple persons in the list, specifically there exists a person with phone number 123 and there is no existing person with phone number 456.
Test case: delete 123
Expected: Contact with phone number 123 is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.
Test case: delete 456
Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.
Other incorrect delete commands to try: delete, delete x, ... (where x is an invalid phone number)
Expected: Similar to previous.
Finding a person by name
Prerequisites: Tether already contains multiple persons in the list, specifically there exists a person with name Alice and there is no existing person with phone number Bob.
Test case: find_name Alice 
Expected: Contact with name Alice is displayed on the list.
Test case: find_name Bob 
Expected: An empty list is displayed.
Finding a person by phone number
Prerequisites: Tether already contains multiple persons in the list, specifically there exists a person with phone number 123 and there is no existing person with phone number 456.
Test case: find_phone 123 
Expected: Contact with phone number 123 is displayed on the list.
Test case: find_phone 456 
Expected: An empty list is displayed.
Finding a person by email
Prerequisites: Tether already contains multiple persons in the list, specifically there exists a person with phone number alice@email.com and there is no existing person with phone number bob@email.com.
Test case: find_email alice@email.com 
Expected: Contact with email alice@email.com is displayed on the list.
Test case: find_email bob@email.com 
Expected: An empty list is displayed.
Updating an applicant's status
Prerequisites: List all persons using the list_persons command. Multiple applicants in the list, specifically there exists an applicant with phone number 123 and an interviewer with phone number 321.
Test case: applicant_status 123 s/accepted
Expected: Status field of applicant with phone number 123 is updated with "accepted". Details of the applicant with their status is shown in the result message.
Test case: applicant_status 321 s/accepted
Expected: Status field of interviewer with phone number 321 does not change. Error message displayed to user stating that only applicants can be given this status.
Test case: applicant_status 123 s/free
Expected: Status field of applicant with phone number 123 does not change. Error message displayed to user stating that applicant status can only be given one of a given set of statuses.
Test case: applicant_status hello s/free
Expected: Error message displayed to user stating that either the command format or parameters are invalid.
Test case: applicant_status 123 s/accepted s/rejected
Expected: Status field of applicant with phone number 123 is updated with "rejected". Details of the applicant with their status is shown in the result message.
Test case: applicant_status 123 s/accepted rejected
Expected: Status field of applicant with phone number 123 does not change. Error message displayed to user stating that applicant status can only be given one of a given set of statuses.
Filtering persons by status
Prerequisites: List all persons using the list_persons command. 4 applicants in the list, specifically there exists 2 applicants with status resume review and pending interview respectively, and 2 interviewers with status free and interview with applicantOneName respectively.
Test case: filter_by_status resume review
Expected: Displayed list of persons is updated to show only 1 applicant with status resume review.
Test case: filter_by_status pending interview
Expected: Displayed list of persons is updated to show only 1 applicant with status pending interview.
Test case: filter_by_status free
Expected: Displayed list of persons is updated to show only 1 interviewer with status free.
Test case: filter_by_status interview with applicantOneName
Expected: Displayed list of persons is updated to show only 1 interviewer with status interview with applicantOneName.
Test case: filter_by_status 1
Expected: Displayed list of persons doesn't change. Error message is displayed stating invalid command format or parameters.
Test case: filter_by_status accepted
Expected: Displayed list of persons doesn't change. Message is displayed stating no persons found with the given status.
Deleting an interview while all interviews are being shown
Prerequisites: List all interviews using the list_interviews command. One interview in the list.
Test case: delete 1
Expected: The only interview is deleted from the list. Details of the deleted interview shown in the status message. Timestamp in the status bar is updated.
Test case: delete 5
Expected: No interview is deleted. Error details shown in the status message. Status bar remains the same.
Other incorrect delete commands to try: delete, delete x, ... (where x is an invalid phone number)
Expected: Similar to previous.
Filtering interviews by date
Prerequisites: Tether already contains multiple interviews in the list, specifically there exists multiple interviews with date 2024-05-05 and there is no existing interview with date 2024-06-06. Additionally, ensure that the original interview list is displayed by using the list_interviews command.
Test case: filter_interviews_by_date 2024-05-05 
Expected: Interviews with date 2024-05-05 is displayed on the list.
Test case: filter_interviews_by_date 2024-06-06 
Expected: A message indicating that no interviews are found is displayed, and the displayed interview list does not change.
View overall statistics
Prerequisites: List all persons using the list_persons command. 4 applicants in the list, specifically there exists 2 applicants with status resume review and pending interview respectively, and 2 interviewers with status free and interview with applicantOneName respectively. List all interviews using the list_interviews command. 1 interview in the list, between the same interviewer whose status is interview with applicantOneName and the same applicant whose status is pending interview.
Test case: view_overall_statistics
Expected: Message is displayed stating 2 applicants total (1 in resume review and 1 in pending interview), 2 interviewers total (1 free and 1 busy), and 1 interview total.
Test case: view_overall_statistics 1
Expected: Same result as Test Case 2. Extraneous parameters are ignored
Saving and loading data from data file.
Prerequisites: The data file exists and is located at data/addressbook.json. Data in data file is valid.
Perform commands that changes data in Tether (Example: add an applicant using add_applicant n/Alice p/123 e/alice@email.com)
Close and launch Tether again. 
Expected: Tether correctly displays the updated data.
Dealing with corrupted data file.
Prerequisites: The data file exists and is located at data/addressbook.json.
Add an invalid character to the addressbook.json (e.g. add a # to the start of file).
Close and launch Tether again. 
Expected: Tether does not display any data and an error message is shown in the terminal.
Dealing with missing data file.
Prerequisites: The data file exists and is located at data/addressbook.json.
Delete the data file located at data/addressbook.json.
Close and launch Tether again. 
Expected: New data file created at data/addressbook.json containing some sample data.
Team Size: 5 members 
Given below are the planned enhancements we plan to implement for our application in the future:
Add group/panel interviews
add_interview command to take in more than one applicants/interviewers phone number.add_interview desc/Group interview date/2024-05-05 st/13:00 et/15:00 a/11111111 a/22222222 i/33333333 i/44444444.
AddApplicantStatusCommand to accommodate custom statuses
AddApplicantStatusCommand strictly checks if the status given by the user matches any of 5 preset statuses. This can be problematic since different companies may have different conventions on applicant statuses and thus require adding applicant statuses to be more flexible.AddApplicantStatusCommand to accommodate custom statuses so long as they still adhere to some basic constraints such as being alphanumeric or ASCII-characters.applicant_status 12345678 s/Stage1 instead of applicant_status 12345678 s/resume review, depending on their specific label for the first stage of the hiring pipeline.Shorthand for commands
fibd 2024-05-05 instead of typing out the whole filter_interviews_by_date 2024-05-05.
PersonCard to accommodate dynamic sizing
PersonCard encapsulating all the fields of a person (name, email, status...) spills out of bounds when the fields have too long values or when statuses stack up vertically (for interviewers). This can be problematic since it impedes the user view of the application and undermines their interaction with it.PersonCards to be more dynamically sized i.e. resize themselves when the information they encapsulate grows out of the current dimensions.
Edit parameters of persons
edit 1 n/Bob can be executed.
Tagging persons
tag 1 late to add a late tag to the first person in the list.
More specific error messages
Separate Applicant/Interviewer list
Delete Interviews when deleting a person
Delete multiple people at once
delete_person command to take in multiple phone numbers at once.delete_person 11111111 22222222 33333333 to delete persons with the respective phone numbers.
Challenges :
addInterviewCommand from scratch.Interview and Person together in the same JSON file with no issues.Interviewer.Effort Required and Difficulty :
Applicant, Interviewer) extending AB3's 1 entity type (Person).Interview).Remark, Type , Status).Person and Interview - 2 list panels compared to 1 in AB3.