Developer Guide
- Setting up, getting started
- Design
- Implementation
- Documentation, logging, testing, configuration, dev-ops
- Appendix: Requirements
-
Appendix: Instructions for manual testing
- Launch and shutdown
- Adding a client
- Editing a client
- Deleting a client
- Filtering clients
- Listing all clients
- Adding a hairdresser
- Editing a hairdresser
- Deleting a hairdresser
- Filtering hairdressers
- Listing all hairdressers
- Adding an appointment
- Editing an appointment
- Deleting an appointment
- Listing all appointments
- Filtering appointments
- Adding Command Shortcuts
- Deleting Command Shortcuts
- Appendix: Effort
Setting up, getting started
Refer to the guide Setting up and getting started.
Design
Architecture

The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.
.puml files used to create diagrams in this document can be found in the diagrams folder. Refer to the PlantUML Tutorial at se-edu/guides to learn how to create and edit diagrams.
Main has two classes called Main and MainApp. It is responsible for,
- At app launch: Initializes the components in the correct sequence, and connects them up with each other.
- At shut down: Shuts down the components and invokes cleanup methods where necessary.
Commons represents a collection of classes used by multiple other components.
The rest of the App consists of 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.
Each of the four components,
- defines its API in an
interfacewith the same name as the Component. - exposes its functionality using a concrete
{Component Name}Managerclass (which implements the corresponding APIinterfacementioned in the previous point.
For example, the Logic component (see the class diagram given below) defines its API in the Logic.java interface and exposes its functionality using the LogicManager.java class which implements the Logic interface.

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_client 1.

The sections below give more details of each component.
UI component

API :
Ui.java
The UI consists of a MainWindow that is made up of parts e.g.CommandBox, ResultDisplay, ClientListPanel, StatusBarFooter etc. All these, including the MainWindow, inherit from the abstract UiPart class.
The UI component uses 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,
- Executes user commands using the
Logiccomponent. - Listens for changes to
Modeldata so that the UI can be updated with the modified data.
Logic component

API :
Logic.java
-
Logicuses theHairStyleXParserclass to parse the user command. - This results in a
Commandobject which is executed by theLogicManager. - The command execution can affect the
Model(e.g. adding a client). - The result of the command execution is encapsulated as a
CommandResultobject which is passed back to theUi. - In addition, the
CommandResultobject can also instruct theUito perform certain actions, such as displaying help to the user.
Given below is the Sequence Diagram for interactions within the Logic component for the execute("delete_client 1") API call.

DeleteClientCommandParser should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Model component

API : Model.java
The Model,
- stores a
UserPrefobject that represents the user’s preferences. - stores the HairStyleX data.
- exposes an unmodifiable
ObservableList<Client>,ObservableList<Hairdresser>,ObservableList<Appointment>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. - does not depend on any of the other three components.
Storage component

API : Storage.java
The Storage component,
- can save
UserPrefobjects in json format and read it back. - can save the HairStyleX data in json format and read it back.
Common classes
Classes used by multiple components are in the seedu.address.commons package.
Implementation
This section describes some noteworthy details on how certain features are implemented.
Command Shortcut feature
This feature allows users to define their own shortcuts for commands that make it easier for them to type.
Current Implementation

This shows the general structure used to implement the Command Alias.
-
Command Word- This is an enumeration that corresponds to each command in the application. -
CommandShortcut- This is a class which stores the hashset of strings that correspond to a particular word. These strings are the command word’s shortcuts. -
CommandShortcutSet- This is a singleton class which stores all the commandShortcut in the application. It is responsible for finding the command word associated with a given input string and to validate whether the new shortcuts are allowed to be saved into the system.
Add Shortcuts
Steps:
- User input a command to the system. It is parsed regularly as before, extracting the string.
-
HairStyleXParserwill pass the string toCommandShortcutSetto retrieve the correspondingCommandWord. - Then, the
CommandWordis processed as usual.
Steps when adding a new shortcut:
- User inputs an
add_shortcutcommand together with the new shortcut, and the command it is associated with. - Upon execution,
CommndShortcutSetwill do validation to check if the validity of the new shortcut. - Finally,
CommandShortcutSetadds the new shortcut to theCommandShortcutit is matched to.
Steps when deleting a new shortcut
- User inputs an
delete_shortcutcommand together with the shortcut to be deleted. - Upon execution,
CommndShortcutSetwill check if the shortcut to be deleted exists and find its correspondingCommandShortcut. - Finally,
CommandShortcutSetdeletes the shortcut from theCommandShortcut.
Steps when listing all shortcuts
- User inputs an
list_shortcutcommand. - Upon execution,
CommndShortcutSetwill retrieve the string representation of all itsCommandShortcutand combine them into one string. - Finally,
CommandShortcutSetreturns the string representation of all its shortcuts.
Saving the Shortcut
JsonHairStyleXStorage saves CommandShortcutSet as a JsonAdapatedCommandShortcutSet together with its JsonAdaptedShortcut.
Upon starting the application, JsonHairStyleXStorage is used to set the singleton CommandShortcutSet.
Hairdresser Management Features
Overview of implementation for Hairdresser
-
Hairdresser- This is an entity class to store information regarding an appointment, such as name, phone, email, gender, title, specialisation. -
Specialisation- This is class containing an enum which represents the specialisations of a hairdresser, which includesColor,Perm,HairExtension,Styling,HairConditioning,Straightening,ScalpTreatment,HairLossTreatment. -
HairdresserID- This is a class which represents the unique ID of an appointment. -
UniqueHairdresserList- This is aUniqueEntityListwhich represents all hairdressers. It ensures that no duplicates of hairdresser can be added and supports add, update, delete of hairdressers. -
JsonAdaptedHairdresser- This class functions as an adapter between Appointment and the Storage layer. It specifies how to convert from an appointment object to a JSON representation and vice versa. It also serves as validation for correct data format when the save file is loaded. -
AddHairdresserCommandParser- This class parses a user input string to aHairdresserCommandobject and performs validation. -
AddHairdresserCommand- This containsexecutemethod which interact with model to perform the add action.
Add Hairdresser Feature
Current implementation
The add_hairdresser command allows the LogicManager to create a new hairdresser and add it to the list of hairdressers.
The following sequence shows the sequence when the add command is execute by the LogicManager:

From the diagram above:
-
LogicManager’sexecuteis called when the user enters a command string into the command box. The string is passed toexecuteas an argument, and it callsparseCommandofHairStyleXParserto parse the command string. -
HairStyleXParserwill initializeAddHairdresserCommandParserand invoke the methodparseto further parse the command -
parseofAddHairdresserCommandParserwill be invoked and passed the parameters of the add hairdresser command. -
If all the arguments of
add_hairdressercommands are valid,AddHairdresserCommandwill be returned to theLogicManager -
LogicMangerwill then callexecutemethod ofAddHairdresserCommand -
AddHairdresserCommandwill calladdHairdresserpassingtoAddas an argument toModeland returns aresultto theLogicManager -
LogicMangerwill then callsaveHairStyleXmethod ofStorage -
A
CommandResultwill be returned at the end.
Appointment feature
This feature represents an appointment between a hairdresser and a client. An appointment consists of a client and a hairdresser. If one of these persons are deleted, the reference will be replaced with a tombstone value indicating a deleted hairdresser/client. A client can have multiple appointments that do not clash, similarly for hairdressers. An appointment must also have a date, time, and status.
Overview of implementation for Appointment
-
Appointment- This is an entity class to store information regarding an appointment, such as hairdresser, client, hairdresser ID, client ID, date, time and status. -
FutureAppointment- This is an entity class which extends Appointment. This class ensures that a newly created appointment is always in the future compared to the system time. -
AppointmentStatus- This is an enum which represents the status of an appointment, which can beactive,completed, ormissed. -
AppointmentID- This is a class which represents the unique ID of an appointment. -
UniqueAppointmentList- This is aUniqueEntityListwhich represents all appointments. It implements features including ensuring that no duplicate appointments can be added. It allows for clients or hairdressers to be replaced or deleted, and updates the relevant appointments. -
JsonAdaptedAppointment- This class functions as an adapter between Appointment and the Storage layer. It specifies how to convert from an appointment object to a JSON representation and vice versa. It also serves as validation for correct data format when the save file is loaded. -
AddAppointmentCommandParser- This class parses a user input string to an AppointmentCommand object. Validation for inputs that do not require access to the model is performed here. -
AddAppointmentCommand- This is where majority of the logic of the add appointment command is performed when theexecutemethod is called. It will access the model to ensure there is no duplicate appointment before adding the appointment to the model.
Add Appointment Feature
Current implementation

From the diagram above:
-
When the user enters a command string into the command box,
LogicManager’sexecuteis called when with the string as an argument. This command string is logged, and then passed toparseCommandofHairStyleXParserwhich parses the command. -
If the command string matches the format for an add appointment command,
HairStyleXParserwill initialize anAddAppointmentCommandParserand invoke the methodparseto further parse the command. -
parseofAddAppointmentCommandParserwill be invoked, and passed the parameters of the add hairdresser command. Here, validation that does not require access to the model is performed, for example, validating the format of hairdresser ID, client ID, date, and time. It ensures that the date/time is valid and is in the future, but does not check whether the hairdresser/client ID corresponds to an actual hairdresser/client (this is validated when the command is executed). -
If the command is valid, a new
AddAppointmentCommandobject is created and returned to theLogicManager. Otherwise, aParseExceptionmay be thrown. -
LogicMangerwill then call theexecutemethod ofAddAppointmentCommand, with themodelas an argument. Here, checks are performed, such as verifying if the hairdresser/client ID corresponds to an actual hairdresser/client, and the appointment is checked against existing appointments in the model to ensure that there are no duplicates or clashes. The appointment object is then added to the model. -
If the command is valid,
AddAppointmentCommandwill calladdAppointmentofModelwith the newly created appointment as an argument, then it will return aCommandResult. Otherwise, aCommandExceptionmay be thrown. -
LogicMangerwill then callsaveHairStyleXmethod ofStorage. This triggers storing information to non-volatile memory using the Storage layer. -
The
CommandResultis returned.
Proposed improvements
-
Currently, validation or updating of appointments based on client/hairdresser ID requires iterating through all appointments to check if they involve the relevant client/hairdresser. Hence this process is slow and runs in O(n) time. It can be improved by implementing two
HashMapsof appointments keyed byClientId/HairdresserIdrespectively. This will allow for the search to be done in O(1) time. We did not implement this feature as it would introduce unnecessary complexity, and the current solution meets the non-functional requirements regarding performance. -
Currently, the order of appointments in the
UniqueAppointmentListis not ordered by time. Hence, sorting appointments by appointment time requires O(n log(n)) time. By maintaining aTreeMapof appointments keyed by time, a list of appointments ordered by appointment time can be generated in O(n), while the next k appointments after a given appointment can be found in O(k log(n)) time. We did not implement this feature as it would introduce unnecessary complexity, and the current solution meets the non-functional requirements regarding performance.
ID and ID Counter
To ensure that every entity within the same class can be distinguished from each other, we have implemented an auto incremental ID system that automatically assigns to a new ID to every entity upon creation.
Reasons for Implementation
Initially, we intended to follow the original AddressBook-3, where each Person would be identified by its shown index in the list shown in the GUI. However, we came to the realisation this approach would confuse the user during this scenario:
- When the user searches for an entity with filters, the filtered list would show indices that are not corresponding to the original list. As such, the user might erroneously key in the wrong index for his/her selected entity.
To tackle this, we concluded that each entity should be identified by a unique ID within its own class, and that this should be displayed to the user and be the primary way of identifying entities for the purposes of editing, deleting, etc.
Current Implementation
ID Class
- The Model package contains an abstract
Idclass, which is essentially a wrapper for an integeridvariable. - The
Idclass also has a concrete method used to verify the validity of theid. - The concrete classes
AppointmentId,ClientId, andHairdresserIdextend the abstract Id class, and reside in their respective packages. - These concrete classes contain their unique error messages which will be shown when an instance is created with an invalid id.

ID Counter Class
- To ensure that the IDs of each entity created are unique, a final class
IdCounteris implemented. - This class is a singleton, and only one instance can exist at any one time.
- It consists of static integer attributes that keep track of the next ID to be generated for the respective entities, namely, Clients, Hairdressers, and Appointments.
-
IdCounterhas methods to generate the next uniqueClientId,HairdresserIdandAppointmentId. - In order to avoid collisions, whenever a
ClientIdis generated, the nextClientIdgenerated will be increased by 1.- Likewise for
HairdresserIdandAppointmentId
- Likewise for

Design Considerations
- Alternative 1 (current implementation): Use 3 separate counters in
IdCounterforClients,Hairdressers,Appointments- Pros: Good OOP Design. Error messages can be abstracted out in their respective ID classes
- Cons: Hassle to keep track of three separate counters, and repetitive to have three separate methods for these counters
- Alternative 2: Use 1 single counter for all entities. Every entity contains an ID instance.
- Pros: Easy to implement, no repetitive code.
- Cons: Bad OOP design. Error messages would be the same for IDs created in all three classes
Usage Scenario
Given below is the example usage scenario that highlights the generation of a new ClientId instance when a new Client is created:

From the diagram above:
-
The user attempts to create a new client by entering the respective fields in the appropriate format, such as
add_client n/John Doe p/98765432 e/johnd@example.com g/M a/311, Clementi Ave 2, #02-25 -
The
AddClientCommandParserextracts the relevant fields from the user input and creates a newClientinstance using theClientconstructor. -
The
Clientconstructor callsIdCounterto generate a newClientIdinstance. -
The returned
ClientIdis stored in this client object, which will be used to create AddClientCommand.
Filter Feature
Since now all entities are categorized into different classes, we want to make sure each class can have their own filter method to easily find an entity or a list of entities within a class.
Reasons for implementation
The filter hairdresser/client feature can be useful when a specific client/hairdresser’s information is needed at the moment. The filter appointment feature can be useful when scheduling a new appointment. By allowing the manager to check the availability of a certain hairdresser, or filter out a list of all appointments in a certain day, it avoids creating conflicting appointments and provides a more efficient way of scheduling appointments.
Use cases
-
a hairdresser or a list of hairdressers can be filtered by their name
-
a client or a list of clients can be filtered by their name
-
an appointment or a list of appointments can be filtered by hairdresser’s id, client’s id, date of appointment, status of appointment or a combination of any of them.
Usage Scenario
Given below is the example usage scenario of filtering a client:
The filter_client Anna command allows the LogicManager to create one or a list of clients whose name contains “Anna”.
The following sequence shows the sequence when the filter command is execute by the LogicManager:

From the diagram above:
-
LogicManager’sexecuteis called whenfilter_client Annais entered and it calls uponparseCommandofHairStyleXParserto parse the command. -
HairStyleXParserwill initializeFilterClientCommandParserand invoke the methodparseto further parse filter client command -
parsewill be invoked and passed the parameters of the filter client command. -
If all the arguments of the filter command are valid,
FilterClientCommandwill be returned to theLogicManager -
LogicMangerwill then callexecutemethod ofFilterClientCommand -
FilterClientCommandwill callupdateFilteredClientListpassingpredicateas an argument toModeland returns aresultto theLogicManager -
LogicMangerwill then callsaveHairStyleXmethod ofStorage -
A
CommandResultwill be returned at the end.
Print Feature
Print feature is to allow users to export data to an easy-to-read format. It allows the lists of hairdressers, clients and appointments to be exported to .csv format.
Current Implementation
PrintCommand - This contains execute method which interact with model to perform the export action.
Some key methods:
-
ExportType- a private enum that encapsulates the type of information to be exported. ContainsExportType.Hairdresser,ExportType.Client,ExportType.Appointment. -
PrintCommand#writeToCsv()- initialises a.csvfile for hairdresser, client or appointment depending on theExportTypeenum given and then writes to it. -
PrintCommand#appendToWriter()- calls the respective functions that convert data to string and appends the returned string to the.csvfiles. -
PrintCommand#removeCommaConflict()- escapes the strings containing commas to avoid formatting error when writing to.csvfiles.
Steps
-
LogicManager’sexecuteis called whenprintis entered and it calls uponparseCommandofHairStyleXParserto parse the command. -
PrintCommandwill be returned to theLogicManagerandLogicMangerwill then callexecutemethod ofPrintCommand. -
executemethod retrieves the threeObservableList:hairdresserList,clientList,appointmentListfrom the model. -
executemethod will then callwriteToCsv3 times in parallel while passing inExportType.Hairdresser,ExportType.Client,ExportType.Appointmentfor each. -
writeToCsvwill createBufferedWriterfor the respective.csvfiles in[root directory of HairStyleX]/data/[ExportType].csvaccording toExportType, then pass the writer andExportTypetoappendToWriter. -
appendToWritercalls the respective functions that converts data to string according toExportType:- For
ExportType.Hairdresser:makeFileCreationTime,makeHairdresserHeaderare called, andprintHairdresseris called for each inhairdresserList. - For
ExportType.Client:makeFileCreationTime,makeClientHeaderare called, andprintClientis called for each inclientList. - For
ExportType.Appointment:makeFileCreationTime,makeAppointmentHeaderare called, andprintAppointmentis called for each inappointmentList.
Each of the methods called by
appendToWriterreturns a string that makes up of one line of the.csvfile, and is appended to the file line by line. After the writing operations are done, the writer is closed. - For
-
A
CommandResultwill be returned at the end.
Activity Diagram

Design Considerations
- Alternative 1 (current implementation): Run
writeToCsvconcurrently- Pros: Shorter overall runtime as the functions are run in parallel.
- Cons: More overhead for the program as there are multi-threads created.
- Alternative 2: Run
writeToCsvsequentially- Pros: Easy to implement, less overhead.
- Cons: Longer runtime as the I/O operations are ran one by one even if it is I/O to different files.
Documentation, logging, testing, configuration, dev-ops
Appendix: Requirements
Product scope
Target user profile:
- managers for budding hair salons
- has a growing number of hairstylists and clients
- has a need to keep track of bookings, store supplies, stylist availability, and client information
- has a desktop at the front desk
- can type fast
- prefers typing to mouse interactions
- is reasonably comfortable using CLI apps
Value proposition: manage hairstylist, client and appointment information faster than the traditional pen and paper
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 the instruction guide | refer to instructions when I forget how to use the App |
* * * |
manager | add a new hairdresser | |
* * * |
receptionist | add a new client | |
* * * |
manager | edit a client/hairdresser | keep my database updated |
* * * |
user | exit the program | |
* * * |
manager | find persons by name | locate a person easily |
* * * |
receptionist | book appointments | |
* * * |
new user | navigate the UI easily | learn how to use the app quickly |
Use cases
(For all use cases below, the System is the HairStyleX application, and the Actor is the user, unless specified otherwise)
(A person refers to either a hairdresser or a client. If a person is used in any of the use cases below,
it means that the use case can be performed similarly on both a hairdresser and a client)
Use case: Delete a person
MSS
- User requests to list people
- HairStyleX shows a list of people
- User requests to delete a specific person in the list
-
HairStyleX deletes the person
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given ID is invalid.
-
3a1. HairStyleX shows an error message.
Use case resumes at step 2.
-
Use case: Add a person
MSS
- User requests to add a specific person in the list
- HairStyleX adds the person
-
HairStyleX shows a success message
Use case ends.
Extensions
-
1a. The input syntax is invalid.
-
1a1. HairStyleX shows an error message.
Use case resumes at step 1.
-
Use case: View people
MSS
- User requests to view all people in the list
-
HairStyleX shows all people
Use case ends.
Extensions
-
1a. The list is empty.
Use case ends.
Use case: Edit Person
MSS
- User requests to view all people in the list
- HairStyleX shows all people
- User requests to edit specific person
-
HairStyleX displays success message with updated person details
Use case ends.
Extensions
-
1a. The list is empty.
Use case ends.
Use case: Find Person
MSS
- User requests to find all people whose name contains a specified
string -
HairStyleX shows all people that has names that contain the specified
stringUse case ends.
Extensions
-
2a. No people has names that contain that specified
string.Use case ends.
Use case: View help
MSS
- User requests to view all valid commands
-
HairStyleX shows all valid commands
Use case ends.
Use case: Add an alias
MSS
- User requests to add a shortcut
-
HairStyleX adds his requested shortcut
Use case ends.
Extensions
-
1a. The shortcut already exists.
-
1a1. HairStyleX shows an error message.
Use case ends.
-
-
1b. The user does not specify the correct old command.
-
1b1. HairStyleX shows an error message.
Use case ends.
-
Non-Functional Requirements
- Should work on any mainstream OS as long as it has Java
11or above installed. - Should be able to hold up to 1000 hairdressers and clients without a noticeable sluggishness in performance for typical usage.
- 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.
- There is no need for internet connection
Glossary
- Mainstream OS: Windows, Linux, Unix, OS-X
- Hairdresser: A hair stylist working in the salon
- Client: A customer that visits the salon
- Person: Can refer to either a hairdresser or a client
- Appointment: A specified time when a hairdresser services a client.
Appendix: Instructions for manual testing
Given below are instructions to test the app manually.
Launch and shutdown
-
Initial launch
-
Download the jar file and copy into an empty folder
-
Launch the jar file with
java -jarcommand
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 with
java -jarcommand.
Expected: The most recent window size and location is retained.
-
Adding a client
-
Adding a client to HairStyleX
-
Prerequisites: Arguments are valid and compulsory parameters are provided. No duplicate client in the list.
-
Test case:
add_client n/John Doe p/98765432 e/johnd@example.com g/M a/311, Clementi Ave 2, #02-25 t/shortHair t/owesMoney
Expected: Adds a client with a unique client ID, with nameJohn Doe, phone98765432, emailjohnd@example.com, genderM, address311, Clementi Ave 2, #02-25, tagsshortHairandowesMoney. -
Test case:
add_client n/John Doe
Expected: No client is added. Error details shown in result display. -
Other incorrect add commands to try:
add_client,add_client g/a,...
Expected: Similar to previous test case.
-
Editing a client
-
Editing a client in HairStyleX
-
Prerequisites: Arguments are valid and compulsory parameters are provided. The client to be edited must be in HairStyleX. A client with client ID 1 must be present in the list.
-
Test case:
edit_client 1 t/preferPerm
Expected: Edits the client with ID of 1 in the displayed client panel with tagpreferPerm. -
Test case:
edit_client t/preferPerm
Expected: No client is edited. Error details shown in result display. -
Other incorrect edit commands to try:
edit_client,edit_client x t/preferPerm(where there are no clients with client ID of x),...
Expected: Similar to previous test case.
-
Deleting a client
-
Deleting a client in HairStyleX
-
Prerequisites: Arguments are valid and compulsory parameters are provided. The client to be deleted must be in HairStyleX. A client with client ID 1 must be present in the list.
-
Test case:
delete_client 1
Expected: Deletes the client with ID of 1 in the displayed client panel. -
Test case:
delete_client -10
Expected: No client is deleted. Error details shown in result display. -
Other incorrect delete commands to try:
delete_client,delete_client x(where there are no clients with client ID of x),...
Expected: Similar to previous test case.
-
Filtering clients
-
Filtering clients in HairStyleX
-
Prerequisites: Arguments are valid and compulsory parameters are provided. There exist client in HairStyleX with name of
Anna. No shortcut has been created forfilter_client. -
Test case:
filter_client anna
Expected: Displays all clients with name ofAnna(case insensitive) in the displayed client list. -
Test case:
filter_client
Expected: No filter is applied. Error details shown in result display. Client panel remains unchanged. -
Other incorrect filter commands to try:
flter_c,filter_c cid/one...
Expected: Similar to previous test case.
-
Listing all clients
-
Listing all clients in HairStyleX
-
Prerequisites: No arguments are required. There exist some clients currently in HairStyleX. No shortcut has been created for
list_client. A validfilter_clientcommand has been called. -
Test case:
list_client
Expected: Lists all clients in the client panel. -
Test case:
lst_client
Expected: Application unable to detect command. Error details shown in result display. Clients panel remains unchanged. -
Other incorrect list commands to try:
list_c,ls_c,...
Expected: Similar to previous test case.
-
Adding a hairdresser
-
Adding a hairdresser to HairStyleX
-
Prerequisites: Arguments are valid and compulsory parameters are provided. No duplicate hairdresser in the list.
-
Test case:
add_hairdresser n/Mary Ng p/98123732 e/maryn@example.com g/F ti/Senior Stylist s/Perm
Expected: Adds a hairdresser with a unique hairdresser ID, with nameMary Ng, phone98123732, emailmaryn@example.com, genderF, titleSenior Stylist, specialisationPerm. -
Test case:
add_hairdresser n/Serena Goh
Expected: No hairdresser is added. Error details shown in result display. -
Other incorrect add commands to try:
add_hairdresser,add_hairdresser s/wash,...
Expected: Similar to previous test case.
-
Editing a hairdresser
-
Editing a hairdresser in HairStyleX
-
Prerequisites: Arguments are valid and compulsory parameters are provided. The hairdresser to be edited must be in HairStyleX. A hairdresser with hairdresser ID 1 must be present in the list.
-
Test case:
edit_hairdresser 1 ti/Apprentice
Expected: Edits the hairdresser with ID of 1 in the displayed hairdresser panel with titleApprentice. -
Test case:
edit_hairdresser ti/Apprentice
Expected: No hairdresser is edited. Error details shown in result display. -
Other incorrect edit commands to try:
edit_hairdresser,edit_hairdresser x s/Perm(where there are no hairdressers with hairdresser ID of x),...
Expected: Similar to previous test case.
-
Deleting a hairdresser
-
Deleting a hairdresser in HairStyleX
-
Prerequisites: Arguments are valid and compulsory parameters are provided. The hairdresser to be deleted must be in HairStyleX. A hairdresser with hairdresser ID 1 must be present in the list.
-
Test case:
delete_hairdresser 1
Expected: Deletes the hairdresser with ID of 1 in the displayed hairdresser panel. -
Test case:
delete_hairdresser -10
Expected: No hairdresser is deleted. Error details shown in result display. -
Other incorrect delete commands to try:
delete_hairdresser,delete_hairdresser x(where there are no hairdressers with hairdresser ID of x),...
Expected: Similar to previous test case.
-
Filtering hairdressers
-
Filtering hairdressers in HairStyleX
-
Prerequisites: Arguments are valid and compulsory parameters are provided. There exist hairdresser in HairStyleX with name of
Anna. No shortcut has been created forfilter_hairdresser. -
Test case:
filter_hairdresser anna
Expected: Displays all hairdressers with name ofAnna(case insensitive) in the displayed hairdresser list. -
Test case:
filter_hairdresser
Expected: No filter is applied. Error details shown in result display. Hairdresser panel remains unchanged. -
Other incorrect filter commands to try:
flter_h,filter_hr cid/one...
Expected: Similar to previous test case.
-
Listing all hairdressers
-
Listing all hairdressers in HairStyleX
-
Prerequisites: No arguments are required. There exist some hairdressers currently in HairStyleX. No shortcut has been created for
list_hairdresser. A validfilter_hairdressercommand has been called. -
Test case:
list_hairdresser
Expected: Lists all hairdressers in the hairdresser panel. -
Test case:
lst_hairdresser
Expected: Application unable to detect command. Error details shown in result display. Hairdressers panel remains unchanged. -
Other incorrect list commands to try:
list_hr,ls_h,...
Expected: Similar to previous test case.
-
Adding an appointment
-
Adding an appointment to HairStyleX
-
Prerequisites: Arguments are valid and compulsory parameters are provided. All appointments have a duration of 2 hours. Hence the end time of an appointment is implicitly two hours after the start time. Appointments involving the same persons (hairdresser or client) should not overlap in time. Equivalently, no person should be simultaneously involved in two appointments. Appointments should only be created in the future. A client with client ID of 1 and a hairdresser with hairdresser ID of 2 currently exists in HairStyleX
-
Test case:
add_appt cid/1 hid/2 d/2020-12-12 t/17:30
Expected: Adds an appointment with a unique appointment id, involving the client with client ID of 1 and hairdresser with hairdresser ID of 2, with a status ofactive -
Test case:
add_appt cid/1 hid/2
Expected: No appointment is added. Error details shown in result display. -
Other incorrect add commands to try:
add_appt,add_appt cid/1 hid/2 d/2002-12-12 t/17:30,...
Expected: Similar to previous test case.
-
Editing an appointment
-
Editing an appointment in HairStyleX
-
Prerequisites: Arguments are valid and compulsory parameters are provided. The appointment to be edited must be in HairStyleX. Only the status of the appointment can be updated. If you wish to change other aspects of an appointment, such as the client/hairdresser/time, simply delete the appointment and create a new one. There exist an appointment in HairStyleX with appointment ID of 1, whose appointment date and time is in the past.
-
Test case:
edit_appt 1 s/COMPLETED
Expected: Edits the appointment with ID of 1 in the displayed appointment panel with the status ofCOMPLETED. -
Test case:
edit_appt s/COMPLETED
Expected: No appointment is edited. Error details shown in result display. -
Other incorrect edit commands to try:
edit_appt,edit_appt x s/COMPLETED(where there are no appointments with appointment ID of x),...
Expected: Similar to previous test case.
-
Deleting an appointment
-
Deleting an appointment in HairStyleX
-
Prerequisites: Arguments are valid and compulsory parameters are provided. The appointment to be deleted must be in HairStyleX. There exist an appointment in HairStyleX with appointment ID of 1.
-
Test case:
delete_appt 1
Expected: Deletes the appointment with ID of 1 in the displayed appointment panel. -
Test case:
delete_appt -10
Expected: No appointment is deleted. Error details shown in result display. -
Other incorrect delete commands to try:
delete_appt,delete_appt x(where there are no appointments with appointment ID of x),...
Expected: Similar to previous test case.
-
Listing all appointments
-
Listing all appointments in HairStyleX
-
Prerequisites: No arguments are required. There exist some appointments currently in HairStyleX. No shortcut has been created for
list_appt. -
Test case:
list_appt
Expected: Lists all appointments in the appointment panel. -
Test case:
lst_appt
Expected: Application unable to detect command. Error details shown in result display. Appointment panel remains unchanged. -
Other incorrect list commands to try:
list_appointment,ls_a,...
Expected: Similar to previous test case.
-
Filtering appointments
-
Filtering appointments in HairStyleX
-
Prerequisites: Arguments are valid and compulsory parameters are provided. There exist appointments in HairStyleX with status of
MISSED. -
Test case:
filter_appt s/missed
Expected: Displays all appointments with status ofMISSEDin the displayed appointment list. -
Test case:
filter_appt 10
Expected: No filter is applied. Error details shown in result display. Appointment panel remains unchanged. -
Other incorrect filter commands to try:
filter_appt d/100000,filter_appt cid/one...
Expected: Similar to previous test case.
-
Adding Command Shortcuts
-
Adding Command Shortcuts in HairStyleX
-
Prerequisites: Shortcut to be added must not already exist in HairStyleX. It must also not contian white lines and have a maximum of 20 letters. Parameters must be provided.
-
Test case:
add_shortcut old/help new/hp
Expected: Shows hp being added as a new shortcut.hpshould now display the help window. -
Test case:
add_shortcut old/help new/exit
Expected: No shortcut is added. Error details shown in result display. -
Other incorrect add shortcut commands to try:
add_shortcut old/ne,add_shortcut new/sdf...
Expected: Similar to previous test case.
-
Deleting Command Shortcuts
-
Adding Command Shortcuts in HairStyleX
- Prerequisites: Shortcut to be delete must already exist in HairStyleX. It must not be the default command.
-
Test case:
delete_shortcut hp
Expected: Shows hp being deleted. -
Test case:
delete_shortcut delete_shortcut
Expected: No shortcut is deleted. Error details shown in result display. - Other incorrect delete shortcut commands to try:
delete_shortcut help,delete_shortcut non_existent_shortcut...
Expected: Similar to previous test case.
Appendix: Effort
Creating HairStyleX required much effort from all the team members. Due to COVID-19, we did not meet up, and had to work around the limitations of virtual collaboration. We persevered, and honed our communication and teamwork skills through this project. Our members made sacrifices to their schedules in order to attend our weekly online meetings. By the end, we achieved excellent communication as a team. Our final product contained around 20KLoC. This showcases our hard work and dedication in creating HairStyleX.
Major Enhancements
HairStyleX has many significant enhancements from AB3. Here are some examples:
-
The Person class was extended into Hairdresser and Client classes, each with their own attributes and validations.
-
The Appointment feature was created to represent an association between a Hairdresser and Client. It includes complex features such as scheduling, status, and conflict detection.
-
Shortcuts were implemented such that users can define their own shortcuts for each command. This feature also supports multiple shortcuts for the same command.
-
Filtering functionality for Hairdresser, Client, and Appointment makes use of the app more practical even with a large data set.
Challenges and considerations
Throughout the development of HairStyleX, we faced many challenges. The following points will describe these challenges what how did we deal with them.
Large number of classes/entities
There was a large increase in the number of entities to implement and manage. We also had to be careful of unwanted interactions between them causing bugs.
While AB3 deals with only one (Person) model object, HairStyleX deals with multiple (Client, Hairdresser, Appointment, Filtered List, Shortcut). Thus, we had to create much functionality from scratch. In order to avoid unwanted interactions, we followed design patterns and used extensive testing, in addition to communicating well to make sure all team members are aware of how functionality contributed by other teammates works.
Revamped UI
The Ui of AB3 only contains one panel (ListPanel). On the other hand, HairStyleX has 3 panels. This added additional complexity. Beyond just duplicating the panels, each panel also incorporated extended functionality of filtering.
Appointment statuses
In our initial prototype, we included the status “cancelled” for appointments. However, after some deliberation we decided to remove it, as this would cause unnecessary implementation complexity (especially due to appointment clash detection), and since a salon could still use the client tag field to mark if a client had regularly cancelled appointments.
Working Process
Our team placed heavy emphasis on communication. and efficient division of workload. With our dedicated members and good team spirit, we were able to produce a great product.
We made use of github’s project boards as task trackers, which allowed efficient division and tracking of work, and also helped each team member visualise the tasks remaining.