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
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
object 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
and Reservation
objects (which are contained in the
UniquePersonList
and UniqueReservationList
objects respectively.
UniquePersonList
objects: one called persons
representing the contacts list and another called archivedPersons
for the archived list.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.Reservation
objects in the same fashion as 'selected' Person
objects.UserPref
object that represents the user’s preferences. This is exposed to the outside as a ReadOnlyUserPref
objects.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.
The implementation of help command is more related to the Ui part of the code compared to other commands as it opens another window to show the command summary. It is facilitated by the handleHelp
method in the MainWindow
class.
The following activity diagram shows how a user views the help window with the help
command.
Given below is an example usage scenario of the help
command.
Step 1. The user launches the application. The user will see the main window of the address book.
Step 2. The user executes the help
command to open the help window. The MainWindow#executeCommand()
will call MainWindow#isShowHelp()
to check if the command to be executed is a help
command. The help
command is detected and causes Model#handleHelp()
to be called which opens the help window.
Step 3. The user looks through the command summary table to find the information needed.
Step 4. The user can copy the website address by clicking Copy URL
button in the help window and navigate to the website for more detailed information.
Note The user can do this anytime as long as the help window is opened.
Step 5. The user can then click the X
on top right corner or pressQ
on keyboard to close the help window.
Aspect: How help window is presented:
Option 1 (current choice): The website address and things to note on top of a command summary table.
Option 2: Only the website address.
The filtering of contacts by tag is facilitated by HasMatchingTagPredicate
. It implements the Predicate<Person>
interface and overrides the test(Person)
method, which is used to decide which Person
s will be displayed after the filter command. The test(Person)
method will return true only if the person has a tag matching every filter tag.
Here is the class diagram for FilterCommand
.
The following sequence diagram shows how a filter command goes through the Logic
component.
Note: The lifeline for FilterCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of diagram.
To safeguard against accidentally clearing the contacts list, the clear
command requires the user to input a confirmation after the initial clear
command. This is performed with the use of the two flags in the ModelManager
class, namely isAwaitingClear
and isConfirmClear
, where their statuses are checked within the execution of the clear
command since the model is passed to the command.
Another flag, previouslyClear
, is used in the LogicManager
class to check if the previous command was a clear
command before handling the current command entered, where it checks if a confirmation "y" is entered.
The following activity diagram summarizes what happens when a user executes a clear
command:
The archive
command essentially removes the person from the UniquePersonList persons
and places the person into the UniquePersonList archivedPersons
in the address book, hiding the person's contact from the main list.
In this example, Irfan was initially in the persons
list, at INDEX
1.
After being archived, Irfan was removed from the persons
list and added to the archivedPersons
list.
The archive
command has its corresponding unarchive
command which conversely removes the person from the archivedPersons
list and adds them into the persons
list inside the address book.
There is also the associated alist
command that displays all the contacts that have been added into the archivedPersons
list.
Archiving in CulinaryContacts has also been implemented in a way that allows for all other commands to be performed on the archived list. This was achieved by modifying the FilteredList<Person> filteredPersons
within the ModelManager
class to dynamically contain either the archived persons or the normal persons. This is because many of the original commands already make use of the filteredPersons
list to execute the commands on. For commands that do not use the filteredPersons
list in their execution, the flag isViewingArchivedList
within the ModelManager
is used instead in order to check if the user is currently viewing the normal persons or the archived persons before performing the command on the corresponding list.
Target user profile:
Value proposition: This app offers streamlined contact and reservation management for small family-owned restaurants, enhancing operational efficiency without covering financial or inventory aspects. It also categorises and stores detailed information about suppliers and the food that they sell to help owners keep track of volatile contacts.
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
* * * | new user | view the user guide easily | learn more about the app as and when I need |
* * * | new user | use a command to exit the program | close the application without moving my mouse |
* * * | new user | add new contacts | save their contact details |
* * * | new user | tag new contacts as 'supplier', 'customer' or 'employees' | keep my contacts list organized |
* * * | new user | view all the contacts that I currently have in my address book | |
* * * | new user | delete existing contacts | remove any unwanted or experimental contacts |
* * * | new user | find specific contact(s) by name | |
* * * | new user | add new reservations | |
* * * | new user | delete existing reservations | |
* * * | new user | archive contacts of employees/suppliers whose contracts have expired | declutter my contacts list |
* * * | new user | unarchive contacts | contact them when the need arises |
* * * | intermediate user | edit existing contacts | update contacts easily without deleting and recreating a new one |
* * * | intermediate user | filter my contacts by specific tag(s) | |
* * * | clumsy user | get a confirmation message when clearing the entire address book | avoid accidentally deleting all contacts |
* * | new user | see a helpful message when my command is not formatted correctly | figure out how to fix my command |
* * | new user | try out the features with some sample data | understand what each feature does |
* * | new user | tag suppliers with the types of food they supply | easily find the right supplier for specific ingredients |
* * | new user | tag employees as 'full time' or 'part time' | easily find the contacts of part time employees during peak period |
* * | forgetful user | see all commands I entered this session | |
* * | clumsy user | undo my last command | so that I can rectify an erroneous command |
* * | user with poor eyesight | view the contact details in a larger window when I click on a contact | more easily see the contact details |
* * | user with poor eyesight | have tags with different colors | easily differentiate tags |
* * | intermediate user | sort reservations by date and time | plan the seating arrangement and kitchen workload effectively |
* * | intermediate user | toggle between previous commands by pressing up and down arrows | save time typing similar commands |
* * | intermediate user | delete multiple contacts in 1 command | remove multiple contacts more quickly |
* * | intermediate user | see upcoming reservations/events in a dashboard | anticipate and prepare for such events |
* * | intermediate user | add special requests (e.g. dietary restrictions) to reservations | provide personalized service to my customers |
* * | expert user | create my own aliases and shortcuts | reduce time spent typing commands |
* | clumsy user | find contacts that match the keyword partially | avoid retyping the command when I make a typo |
* | intermediate user | import contacts from other sources | quickly populate the app with existing information |
* | intermediate user | have autocomplete when typing commands | finish typing the command faster |
* | intermediate user | automatically create a blank email addressed to a contact when I click on his/her email field | email contacts more quickly |
* | expert user | access the app from multiple devices | manage my contacts and reservations on the go |
(For all use cases below, the System is CulinaryContacts
and the Actor is the user
, unless specified otherwise)
Use case: UC01 - View help
MSS
User requests to see the help menu.
CulinaryContacts opens up the help window, displaying the command summary.
Use case ends.
Use case: UC02 - Add a person
MSS
User requests to add a new person.
CulinaryContacts adds the new person to the displayed person list.
Use case ends.
Extensions
1a. The provided field(s) is/are invalid.
1a1. CulinaryContacts shows an error message.
Use case resumes from step 1.
1b. Compulsory field(s) is/are missing.
1b1. CulinaryContacts shows an error message.
Use case resumes from step 1.
Use case: UC03 - List all persons in contacts list
MSS
User requests to show all persons in the contacts list.
CulinaryContacts shows all persons in the contacts list.
Use case ends.
Use case: UC04 - Edit a person
MSS
User requests to list all persons in the contacts list (UC03) or list all persons in the archived list (UC09).
User requests to edit the fields of a specific person in the displayed person list.
CulinaryContacts edits the fields of the person.
Use case ends.
Extensions
1a. The list is empty.
Use case ends.
2a. The given index is invalid.
2a1. CulinaryContacts shows an error message.
Use case resumes from step 2.
2b. The new field value(s) is/are invalid.
2b1. CulinaryContacts shows an error message.
Use case resumes from step 2.
2c. No fields to edit are provided.
2c1. CulinaryContacts shows an error message.
Use case resumes from step 2.
Use case: UC05 - Find a person
MSS
User requests to list all persons in the contacts list (UC03) or list all persons in the archived list (UC09).
User requests to find all persons in the displayed person list with names matching the input keyword(s).
CulinaryContacts shows all persons in the displayed person list with matching names.
Use case ends.
Extensions
1a. No keywords are provided.
1a1. CulinaryContacts shows an error message.
Use case resumes from step 1.
Use case: UC06 - Filter persons by tag
MSS
User requests to list all persons in the contacts list (UC03) or list all persons in the archived list (UC09).
User requests to find all persons in the displayed person list with specific tag(s).
CulinaryContacts shows all persons in the displayed person list matching all tags entered.
Use case ends.
Extensions
1a. No tags are provided.
1a1. CulinaryContacts shows an error message.
Use case resumes from step 1.
Use case: UC07 - Delete a person
MSS
User requests to list all persons in the contacts list (UC03) or list all persons in the archived list (UC09).
User requests to delete a specific person in the displayed person list.
CulinaryContacts deletes the person.
Use case ends.
Extensions
1a. The displayed person list is empty.
Use case ends.
2a. The given index is invalid.
2a1. CulinaryContacts shows an error message.
Use case resumes from step 2.
Use case: UC08 - Clear all entries
MSS
User requests to clear all entries.
CulinaryContacts asks for confirmation to clear all entries.
User confirms to clear all entries.
CulinaryContacts clears all entries.
Use case ends.
Extensions
2a. Confirmation is not given.
2a1. CulinaryContacts cancels the clear action.
Use case ends.
Use case UC09: List all persons in archived list
MSS
User requests to show all contacts in the archived list.
CulinaryContacts shows all persons in the archived list.
Use case ends.
Use case UC10: Archive a person
MSS
User requests to list all persons in the contacts list (UC03).
User requests to archive a specific person in the contacts list.
CulinaryContacts archives the person.
Use case ends.
Extensions
1a. The list is empty.
Use case ends.
2a. The given index is invalid.
2a1. CulinaryContacts shows an error message.
Use case resumes at step 2.
Use case: UC11 - Unarchive a person
MSS
User requests to list all persons in archived list (UC09).
User requests to unarchive a specific person in the archived list.
CulinaryContacts unarchives the person.
Use case ends.
Extensions
1a. The list is empty.
Use case ends.
2a. The given index is invalid.
2a1. CulinaryContacts shows an error message.
Use case resumes at step 2.
Use case UC12: Add a reservation
MSS
User requests to add a new reservation for a person.
CulinaryContacts adds the new reservation to the reservations list.
Use case ends.
Extensions
1a. The provided field(s) is/are invalid.
1a1. CulinaryContacts shows an error message.
Use case resumes from step 1.
1b. Compulsory field(s) is/are missing.
1b1. CulinaryContacts shows an error message.
Use case resumes from step 1.
1c. Another reservation made by the same person, on the same date, and at the same time already exists in the reservations list.
1c1. CulinaryContacts shows an error message.
Use case resumes from step 1.
Use case UC13: Delete a reservation
MSS
User requests to delete a specific reservation in the reservations list.
CulinaryContacts deletes the reservation.
Use case ends.
Extensions
1a. The reservations list is empty.
Use case ends.
2a. The given index is invalid.
2a1. CulinaryContacts shows an error message.
Use case resumes from step 2.
Use case UC14: Sort reservations
MSS
User requests to sort all reservations.
CulinaryContacts shows a sorted list of all reservations.
Use case ends.
Use case UC15: Exit program
MSS
User requests to exit the program.
CulinaryContacts exits.
Use case ends
11
or above installed.Given 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
a. Download the jar file and copy into an empty folder.
b. Double-click the jar file.
Expected: Shows the GUI with a set of sample contacts. The window size may not be optimal.
Saving window preferences
a. Resize the window to an optimal size. Move the window to a different location. Close the window.
b. Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
Viewing the contacts list
a. Prerequisites: None
b. Test case: list
Expected: The contacts list is shown. The ‘contacts’ tab is underlined indicating that it is the current active tab.
Viewing the archived list
a. Prerequisites: None
b. Test case: alist
Expected: The archived list is shown. The ‘archived’ tab is underlined indicating that it is the current active tab.
Removing all persons and reservations from CulinaryContacts.
a. Prerequisites: None
b. Test case: clear
, followed by n
or any other input.
Expected: All persons and reservations remain unchanged. A Clear cancelled!
message will be shown in the result display.
c. Test case: clear
, followed by y
.
Expected: All persons and reservations will be deleted. A Clear successful!
message will be shown in the result display.
Opening the help window to view the command summary
a. Prerequisites: The help window is not open.
b. Test case: help
Expected: The help window is opened.
c. Test case: Press F1
Expected: The help window is opened.
d. Test case: Click Help
button at the top, then click Help F1
button in the drop-down menu.
Expected: The help window is opened.
Copying the user guide website address
a. Prerequisites: The help window is opened.
b. Test case: Click on the Copy URL
button, then paste it in the address bar of an external web browser.
Expected: The website address is copied to clipboard and the website is accessible.
Closing the help window
a. Prerequisites: The help window is opened.
b. Test case: Press q
Expected: The help window is closed.
c. Test case: Click the X
button on top right corner of the help window.
Expected: The help window is closed.
Archiving a person while viewing the contacts list
a. Prerequisites: The user is viewing the contacts list. There is at least 1 person in the contacts list.
b. Test case: archive 1
, followed by alist
Expected: The archived person is removed from the contacts list. This archived contact can be seen by executing the alist
command to view the archived list. Details of the archived contact are shown in the status message.
c. Test case: archive 0
Expected: No person is archived. Result display shows error message: Invalid command format!
.
d. Other incorrect delete commands to try: archive
, archive x
, ...
(where x is larger than list size)
Expected: Similar to previous.
Archiving a person while viewing the archived list
a. Prerequisites: The user is viewing the archived list. There is at least 1 person in the archived list.
b. Test case: archive 1
Expected: Result display shows error message: This command can only be used while viewing the active contacts list
. No person is archived.
Unarchiving a person while viewing the archived list
a. Prerequisites: List all archived persons using the alist
command. There is at least 1 person in the archived list.
b. Test case: unarchive 1
, followed by list
Expected: First contact is removed from the archived list. This unarchived contact can be seen by executing the list
command to view the contacts list. Details of the unarchived contact are shown in the status message.
c. Test case: unarchive 0
Expected: No person is unarchived. Result display shows error message: Invalid command format!
.
d. Other incorrect delete commands to try: unarchive
, unarchive x
, ...
(where x is larger than list size)
Expected: Similar to previous.
Unarchiving a person while viewing the contacts list
a. Prerequisites: The user is viewing the contacts list. There is at least 1 person in the contacts list.
b. Test case: unarchive 1
Expected: Result display shows error message: This command can only be used while viewing the archived list
. No person is unarchived.
Adding a new reservation for the first person in the displayed person list.
a. Prerequisites: There is at least 1 person in the displayed person list. There are no existing reservations made by the first person on 17/04/2024 at 6.00pm.
b. Test case: rsv 1 d/2024-04-17 t/1800 p/8
Expected: A new reservation is added to the reservations list. Name and phone number of the person, as well as date, time and number of people will be displayed in the reservation card.
Adding a duplicate reservation with the same date and time
a. Prerequisites: There is at least 1 person in the displayed person list. There is an existing reservation made by the first person on 17/04/2024 at 6.00pm.
b. Test case: rsv 1 d/2024-04-17 t/1800
p/5
Expected: Result display shows error message: This person has already made a reservation at this date and timing in the address book
. No new reservation is added to the reservations list.
Deleting the first reservation in the reservations list
a. Prerequisites: There is at least 1 reservation in the reservations list.
b. Test case: rsvdel 1
Expected: First reservation is deleted from the reservations list. Details of the reservation shown in the result display.
c. Test case: rsvdel 1000
Expected: Result display shows error message: The reservation index provided is invalid
. No reservation is deleted from the reservations list.
Sorting all reservations
a. Prerequisites: Multiple reservations in the reservations list. There is at least 1 non-expired reservation and 1 expired reservation in the reservations list. There is at least 1 person in the displayed person list.
b. Test case: rsv 1 d/2024-09-25 t/1800 p/4
, followed by rsvsort
Expected: The new reservation is added to the bottom of the reservations list. After the rsvsort
command is called, the reservations list is now sorted in the correct order.
Team size: 4
NAME
of the person preventing addition of duplicate names. This is too restrictive as persons with the same name are quite common in reality. The user may also want to only record the first or last name which may cause more duplicate names. We plan to introduce a new ID
field as a unique identifier for each contact, which will allow duplicate names to be recorded.NAME
or PHONE NUMBER
in the contacts list, these changes are not reflected in existing reservations made for that contact. As a result, the reservation records become inconsistent with the updated contact details. We plan to enhance this feature by ensuring that any updates to a contact in the contacts or archived list will automatically update the contact's details in the reservations list. For example, if the contact John Doe's phone number is updated, all existing reservations under John Doe's name will automatically reflect this new phone number.NAME
, PHONE
, EMAIL
and ADDRESS
of the contact as all these fields are mandatory. This requirement can create difficulties if the user lacks information for all these fields. We plan to make the PHONE
, EMAIL
and ADDRESS
fields optional, so that it is more convenient for the user to add new contacts.INDEX
is less than 1 or greater than 2147483647: The current error message which shows when the INDEX
is less than 1 and exceeds the maximum integer size is ‘Invalid command format’, which might not be clear for the user. We plan to show the error message The person index provided is invalid
for these cases.