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
interface
with the same name as the Component. - exposes its functionality using a concrete
{Component Name}Manager
class (which implements the corresponding APIinterface
mentioned 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
Logic
component. - Listens for changes to
Model
data so that the UI can be updated with the modified data.
Logic component
API :
Logic.java
-
Logic
uses theHairStyleXParser
class to parse the user command. - This results in a
Command
object 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
CommandResult
object which is passed back to theUi
. - In addition, the
CommandResult
object can also instruct theUi
to 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
UserPref
object 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
UserPref
objects 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.
-
HairStyleXParser
will pass the string toCommandShortcutSet
to retrieve the correspondingCommandWord
. - Then, the
CommandWord
is processed as usual.
Steps when adding a new shortcut:
- User inputs an
add_shortcut
command together with the new shortcut, and the command it is associated with. - Upon execution,
CommndShortcutSet
will do validation to check if the validity of the new shortcut. - Finally,
CommandShortcutSet
adds the new shortcut to theCommandShortcut
it is matched to.
Steps when deleting a new shortcut
- User inputs an
delete_shortcut
command together with the shortcut to be deleted. - Upon execution,
CommndShortcutSet
will check if the shortcut to be deleted exists and find its correspondingCommandShortcut
. - Finally,
CommandShortcutSet
deletes the shortcut from theCommandShortcut
.
Steps when listing all shortcuts
- User inputs an
list_shortcut
command. - Upon execution,
CommndShortcutSet
will retrieve the string representation of all itsCommandShortcut
and combine them into one string. - Finally,
CommandShortcutSet
returns 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 aUniqueEntityList
which 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 aHairdresserCommand
object and performs validation. -
AddHairdresserCommand
- This containsexecute
method 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
’sexecute
is called when the user enters a command string into the command box. The string is passed toexecute
as an argument, and it callsparseCommand
ofHairStyleXParser
to parse the command string. -
HairStyleXParser
will initializeAddHairdresserCommandParser
and invoke the methodparse
to further parse the command -
parse
ofAddHairdresserCommandParser
will be invoked and passed the parameters of the add hairdresser command. -
If all the arguments of
add_hairdresser
commands are valid,AddHairdresserCommand
will be returned to theLogicManager
-
LogicManger
will then callexecute
method ofAddHairdresserCommand
-
AddHairdresserCommand
will calladdHairdresser
passingtoAdd
as an argument toModel
and returns aresult
to theLogicManager
-
LogicManger
will then callsaveHairStyleX
method ofStorage
-
A
CommandResult
will 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 aUniqueEntityList
which 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 theexecute
method 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
’sexecute
is called when with the string as an argument. This command string is logged, and then passed toparseCommand
ofHairStyleXParser
which parses the command. -
If the command string matches the format for an add appointment command,
HairStyleXParser
will initialize anAddAppointmentCommandParser
and invoke the methodparse
to further parse the command. -
parse
ofAddAppointmentCommandParser
will 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
AddAppointmentCommand
object is created and returned to theLogicManager
. Otherwise, aParseException
may be thrown. -
LogicManger
will then call theexecute
method ofAddAppointmentCommand
, with themodel
as 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,
AddAppointmentCommand
will calladdAppointment
ofModel
with the newly created appointment as an argument, then it will return aCommandResult
. Otherwise, aCommandException
may be thrown. -
LogicManger
will then callsaveHairStyleX
method ofStorage
. This triggers storing information to non-volatile memory using the Storage layer. -
The
CommandResult
is 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
HashMaps
of appointments keyed byClientId
/HairdresserId
respectively. 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
UniqueAppointmentList
is not ordered by time. Hence, sorting appointments by appointment time requires O(n log(n)) time. By maintaining aTreeMap
of 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
Id
class, which is essentially a wrapper for an integerid
variable. - The
Id
class also has a concrete method used to verify the validity of theid
. - The concrete classes
AppointmentId
,ClientId
, andHairdresserId
extend 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
IdCounter
is 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.
-
IdCounter
has methods to generate the next uniqueClientId
,HairdresserId
andAppointmentId
. - In order to avoid collisions, whenever a
ClientId
is generated, the nextClientId
generated will be increased by 1.- Likewise for
HairdresserId
andAppointmentId
- Likewise for
Design Considerations
- Alternative 1 (current implementation): Use 3 separate counters in
IdCounter
forClient
s,Hairdresser
s,Appointment
s- 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
AddClientCommandParser
extracts the relevant fields from the user input and creates a newClient
instance using theClient
constructor. -
The
Client
constructor callsIdCounter
to generate a newClientId
instance. -
The returned
ClientId
is 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
’sexecute
is called whenfilter_client Anna
is entered and it calls uponparseCommand
ofHairStyleXParser
to parse the command. -
HairStyleXParser
will initializeFilterClientCommandParser
and invoke the methodparse
to further parse filter client command -
parse
will be invoked and passed the parameters of the filter client command. -
If all the arguments of the filter command are valid,
FilterClientCommand
will be returned to theLogicManager
-
LogicManger
will then callexecute
method ofFilterClientCommand
-
FilterClientCommand
will callupdateFilteredClientList
passingpredicate
as an argument toModel
and returns aresult
to theLogicManager
-
LogicManger
will then callsaveHairStyleX
method ofStorage
-
A
CommandResult
will 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.csv
file for hairdresser, client or appointment depending on theExportType
enum given and then writes to it. -
PrintCommand#appendToWriter()
- calls the respective functions that convert data to string and appends the returned string to the.csv
files. -
PrintCommand#removeCommaConflict()
- escapes the strings containing commas to avoid formatting error when writing to.csv
files.
Steps
-
LogicManager
’sexecute
is called whenprint
is entered and it calls uponparseCommand
ofHairStyleXParser
to parse the command. -
PrintCommand
will be returned to theLogicManager
andLogicManger
will then callexecute
method ofPrintCommand
. -
execute
method retrieves the threeObservableList
:hairdresserList
,clientList
,appointmentList
from the model. -
execute
method will then callwriteToCsv
3 times in parallel while passing inExportType.Hairdresser
,ExportType.Client
,ExportType.Appointment
for each. -
writeToCsv
will createBufferedWriter
for the respective.csv
files in[root directory of HairStyleX]/data/[ExportType].csv
according toExportType
, then pass the writer andExportType
toappendToWriter
. -
appendToWriter
calls the respective functions that converts data to string according toExportType
:- For
ExportType.Hairdresser
:makeFileCreationTime
,makeHairdresserHeader
are called, andprintHairdresser
is called for each inhairdresserList
. - For
ExportType.Client
:makeFileCreationTime
,makeClientHeader
are called, andprintClient
is called for each inclientList
. - For
ExportType.Appointment
:makeFileCreationTime
,makeAppointmentHeader
are called, andprintAppointment
is called for each inappointmentList
.
Each of the methods called by
appendToWriter
returns a string that makes up of one line of the.csv
file, and is appended to the file line by line. After the writing operations are done, the writer is closed. - For
-
A
CommandResult
will be returned at the end.
Activity Diagram
Design Considerations
- Alternative 1 (current implementation): Run
writeToCsv
concurrently- 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
writeToCsv
sequentially- 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
string
Use 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
11
or 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 -jar
command
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 -jar
command.
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
, tagsshortHair
andowesMoney
. -
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_client
command 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_hairdresser
command 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 ofMISSED
in 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.hp
should 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.