Setting up, getting started

Refer to the guide Setting up and getting started.


Design

:bulb: Tip: The .puml files used to create diagrams are in this document docs/diagrams folder. Refer to the PlantUML Tutorial at se-edu/guides to learn how to create and edit diagrams.

Architecture

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.

  • At app launch, it initializes the other components in the correct sequence, and connects them up with each other.
  • At shut down, it shuts down the other components and invokes cleanup methods where necessary.

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),

  • defines its API in an interface with the same name as the Component.
  • implements its functionality using a concrete {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.

UI component

The API of this component is specified in Ui.java

Structure of the UI Component

The UI consists of a MainWindow that is made up of parts e.g. CommandBox, ResultDisplay, PersonListPanel, StatusBarFooter, SidebarPanel, TemplateViewPanel 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.

New UI Components in OnlySales:

  • ImportWindow - A separate window for importing customer data from clipboard
  • SidebarPanel - A panel that contains the StatusViewPanel and TagsViewPanel for displaying active filters
  • StatusViewPanel - Displays all currently active status filters applied through the find command
  • TagsViewPanel - Displays all currently active tag filters applied through the find command
  • TemplateViewPanel - Displays and manages email templates for different customer status types

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,

  • executes user commands using the Logic component.
  • listens for changes to Model data so that the UI can be updated with the modified data.
  • keeps a reference to the Logic component, because the UI relies on the Logic to execute commands.
  • depends on some classes in the Model component, as it displays Person objects residing in the Model.
  • The SidebarPanel contains nested UI components (StatusViewPanel and TagsViewPanel) that update automatically when filter commands are executed.
  • The ImportWindow interacts with the Logic component to process imported customer data.

Logic component

API : Logic.java

Here’s a (partial) class diagram of the new Logic component, with additional classes to the AB3 being shown in light blue:

The sequence diagram below illustrates the interactions within the Logic component, taking execute("delete 1") API call as an example.

Interactions Inside the Logic Component for the `delete 1` Command

How the Logic component works:

  1. When 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.
  2. This results in a Command object (more precisely, an object of one of its subclasses e.g. DeleteCommand) which is executed by the LogicManager.
  3. The command can communicate with the Model when it is executed (e.g. to delete a person).
    Note that although this is shown as a single step in the diagram above (for simplicity), in the code it can take several interactions (between the command object and the Model) to achieve.
  4. The result of the command execution is encapsulated as a 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:

  • When called upon to parse a user command, the 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.
  • All XYZCommandParser classes (e.g. AddCommandParser, DeleteCommandParser, …) inherit from the Parser interface so that they can be treated similarly where possible e.g, during testing.

Model component

API : Model.java

The Model component,

  • stores the address book data i.e. all Person objects (which are contained in a UniquePersonList object).
  • stores the currently ‘selected’ 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.
  • stores a UserPrefs object that represents the user’s preferences. This is exposed to the outside as ReadOnlyUserPrefs objects.
  • stores view state objects (StatusViewState, TagsViewState, TemplateViewState) as observable properties that track the current UI filter and display states. These are exposed as ReadOnlyObjectProperty instances that the UI can observe for reactive updates.
  • does not depend on any of the other three components (as the Model represents data entities of the domain, they should make sense on their own without depending on other components)

Storage component

API : Storage.java

The Storage component,

  • can save both address book data and user preference data in JSON format, and read them back into corresponding objects.
  • inherits from both AddressBookStorage, UserPrefStorage and TemplateStorage, which means it can be treated as either one (if only the functionality of only one is needed).
  • uses a facade pattern and delegates storage operations to three specialized storage implementations:
    • JsonAddressBookStorage — Handles address book persistence using JSON format
    • JsonUserPrefsStorage — Handles user preferences persistence using JSON format
    • TemplateStorageManager — Handles email templates persistence as individual text files

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.

Status View and Tag View Feature

Overview

The Status View and Tag View feature provides visual feedback to users about which filters are currently active when using the find command. When users search for customers by status (e.g. find s:Contacted) or tags (e.g. find t:friends), dedicated UI panels automatically update to display the active filters, making it easy to see what subset of data is being viewed.

Architecture

The implementation follows the Observer Pattern using JavaFX’s property binding mechanism to automatically sync UI state with model state.

Status/Tag View Class Diagram

Key Components:

  1. Model Layer:

    • StatusViewState and TagsViewState: Immutable state objects that represent current filter states
    • ModelManager: Stores these states as ObjectProperty objects and exposes them via the Model interface
  2. Logic Layer:

    • Logic interface: Exposes getStatusViewStateProperty() and getTagsViewStateProperty() methods
    • FindCommand: Updates the view states in Model when executing filter operations
  3. UI Layer:

    • StatusViewPanel and TagsViewPanel: Observe the properties exposed by Logic and automatically update their display
    • UI components depend on the Logic abstraction, maintaining proper architectural layering

Implementation Details

The following sequence diagram shows how the view states are updated when a user executes find s:Contacted t:friends:

Status/Tag View Sequence Diagram

Step-by-step flow:

  1. User executes a find command with status/tag filters
  2. LogicManager parses and creates a FindCommand
  3. FindCommand.execute() is called:
    • Updates the filtered person list in Model
    • Calls model.setStatusViewState() with the appropriate StatusViewState
    • Calls model.setTagsViewState() with the appropriate TagsViewState
  4. ModelManager updates its ObjectProperty fields
  5. JavaFX property listeners in StatusViewPanel and TagsViewPanel are automatically triggered
  6. UI panels update their labels to display the active filters

Design Considerations

Aspect: How to represent filter state in the UI

  • Alternative 1 (Chosen): Use explicit state objects (StatusViewState, TagsViewState) to track user intent

    • Pros:
      • UI displays what the user explicitly searched for (intent), not just the consequence. Handles edge cases where multiple filter combinations produce the same result. Clear separation of concerns.
    • Cons:
      • Additional state management complexity, requires synchronization between filter predicates and view states.
  • Alternative 2: Derive view state from FilteredPersonList

    • Pros:
      • Single source of truth, no state synchronization needed, simpler implementation.
    • Cons:
      • UI displays consequence rather than intent. For example, if a user searches for s:Contacted but no customers have that status, the filtered list would be empty and the UI couldn’t distinguish whether filters were applied or not.
      • Cannot accurately determine which specific filters were applied if multiple filter combinations produce the same filtered list.

Aspect: How to communicate filter state to UI

  • Alternative 1: Direct UI method calls from Command classes
    • Pros:
      • Simpler to understand.
      • Explicit control flow.
    • Cons:
      • Violates architectural boundaries (Logic calling UI directly) and tight coupling.

Aspect: Where to store view state

  • Alternative 1 (Chosen): Store in Model layer

    • Pros:
      • Centralized state management, follows MVC pattern.
      • Greater Testability as we can mock the data.
    • Cons:
      • Model becomes slightly more complex.
  • Alternative 2: Store in UI components only

    • Pros:
      • Simpler Model layer.
    • Cons:
      • State is scattered.
      • Harder to test, UI must deduce state from filtered list.

Template Feature

Implementation Details

The template feature allows salespersons to create, edit, and copy email templates associated with different contact statuses. This streamlines the process of sending personalized emails to contacts at different stages of the sales process.

The template mechanism is facilitated by TemplateStorage, TemplateCommand, and TemplateViewState. It uses the following key components:

  • TemplateStorage — Interface for reading and writing template files.
  • TemplateStorageManager — Concrete implementation that stores templates as text files in the data directory.
  • TemplateCommand — Command that handles both opening templates for editing and saving edited templates.
  • TemplateViewState — Model class that tracks the currently displayed template (status and content).
  • TemplateViewPanel — UI component that displays the template editor.

These operations are exposed in the Model interface as:

  • Model#getTemplateViewStateProperty() — Returns an observable property for the current template state.
  • Model#setTemplateViewState(TemplateViewState) — Updates the template view state.

And in the Storage interface as:

  • Storage#readTemplate(Status) — Reads a template for a specific status.
  • Storage#saveTemplate(Status, String) — Saves template content for a specific status.

Given below is an example usage scenario and how the template mechanism behaves at each step.

Step 1. The salesperson launches the application. The TemplateStorage is initialized and ready to read/write template files in the data directory. No template is currently being viewed, so TemplateViewState is null.

TemplateState0

Step 2. The salesperson executes template s:CONTACTED to open the template for contacted clients. The TemplateCommand calls Storage#readTemplate(Status.CONTACTED) to retrieve the template content (or default template if none exists), then calls Model#setTemplateViewState(TemplateViewState) to display it in the template editor.

TemplateState1

Step 3. The salesperson edits the template content directly in the TemplateViewPanel. The changes are stored in the UI component but not yet saved to persistent storage. The model’s TemplateViewState is updated when the salesperson types.

TemplateState2

:information_source: Note: The template content is only saved to storage when the template save command is explicitly executed. Simply editing the text does not persist changes.

Step 4. The salesperson decides to save the edited template by executing template save. The TemplateCommand retrieves the current TemplateViewState from the model, extracts the status and content, and calls Storage#saveTemplate(Status, String) to persist the changes.

TemplateState3

:information_source: Note: If the salesperson switches to a different view (e.g. executes list or find) without saving, the edited content is discarded and not persisted.

The following sequence diagram shows how the template open operation goes through the Logic component:

TemplateOpenSequenceDiagram

The following sequence diagram shows how the template save operation works:

TemplateSaveSequenceDiagram

Step 5. The salesperson can also copy a template directly to the clipboard without opening the editor by executing template copy s:CONTACTED. This reads the template and places it on the system clipboard for pasting into an email client.

TemplateState4

The following sequence diagram shows how the copy operation works:

TemplateCopySequenceDiagram

The template feature supports all six contact statuses (UNCONTACTED, CONTACTED, REJECTED, ACCEPTED, UNREACHABLE, BUSY), with each status having its own independent template file.

Default Templates and Blank Content Handling

The template system includes automatic handling of blank or invalid template content to ensure data integrity:

Default Templates:

  • Each status has a default template in the format: “This is the default template for status [StatusName]”
  • Default templates are automatically created when:
    • A template file doesn’t exist for a status
    • A template file contains only whitespace or is empty
    • A user attempts to save blank content

Blank Content Detection:

  • The system checks for blank content (null, empty string, or whitespace-only) in two places:
    1. During Save (saveTemplate): When a user saves a template with blank content, it’s automatically replaced with the default template
    2. During Read (readTemplate): When reading a template file that contains blank content, the file is replaced with the default template

User Feedback:

  • When saving blank content, users see: “Detected empty template as input, saving as the default template instead.”
  • This prevents accidental deletion of templates and ensures meaningful content is always available

Implementation Details:

  • TemplateStorageManager.saveTemplate() uses String.isBlank() to detect blank content before writing
  • TemplateStorageManager.readTemplate() validates file content after reading and replaces blank files
  • TemplateCommand.executeSave() detects blank content to show appropriate user feedback
  • All operations maintain consistency across template copy, open, and save workflows

Design Considerations

Aspect: How templates are stored

  • Alternative 1 (current choice): Store each template as a separate text file per status

    • Pros:
      • Simple to implement
      • Easy to manually edit templates outside the application
      • Human-readable format.
    • Cons:
      • Requires file I/O for each template operation, more potential for file system errors.
  • Alternative 2: Store all templates in a single JSON file

    • Pros:
      • Single file to manage, consistent with address book storage format, easier to backup.
    • Cons:
      • More complex serialization/deserialization.
      • Harder for users to manually edit, risk of corrupting all templates if JSON is malformed.

Aspect: When to save template changes:

  • Alternative 1 (current choice): Require explicit template save command

    • Pros:
      • Gives users control over when changes are persisted, prevents accidental overwrites.
    • Cons:
      • Users might forget to save and lose their edits.
  • Alternative 2: Auto-save on every keystroke or after a delay

    • Pros:
      • No risk of losing work, more convenient for users.
    • Cons:
      • May cause performance issues with frequent file I/O
      • Harder to implement “cancel” functionality, causing user to save incomplete/incorrect templates.

Aspect: Template editor vs. clipboard copy:

  • Current implementation: Provides both template s:STATUS (opens editor) and template copy s:STATUS (direct clipboard copy)

    • Pros:
      • Flexibility for different workflows - edit for customization, copy for quick use.
    • Cons:
      • Two different commands to learn and maintain.
  • Alternative: Only provide editor, remove direct copy command

    • Pros:
      • Simpler command set, encourages review before sending
    • Cons:
      • Less efficient for users who want to quickly copy without viewing.

Aspect: How to handle blank/empty template content:

  • Alternative 1 (current choice): Automatically replace blank content with default template

    • Pros:
      • Prevents accidental deletion of templates, ensures meaningful content always exists.
      • Provides clear fallback behavior, maintains data integrity across all operations.
    • Cons:
      • Users cannot intentionally create truly empty templates.
      • Slightly more complex implementation with validation in both read and write paths.
  • Alternative 2: Allow saving and storing blank templates

    • Pros: Simpler implementation, gives users complete control, allows intentional blank templates.
    • Cons: Users could accidentally delete all content and lose their work, template copy operations would copy whitespace/empty content (confusing UX), no clear indication when template is blank vs. intentionally empty, violates principle of least surprise.
  • Alternative 3: Prevent saving blank templates with error message

    • Pros:
      • Prevents data loss.
      • Clear user feedback about invalid input.
    • Cons:
      • Frustrating UX when users want to reset to default (would need separate command).
      • Doesn’t handle externally-modified files with blank content, no automatic recovery from corrupted template files.

Status Feature

Implementation Details

The status command feature allows users to set and track the contact status of each person in the address book. The status command is facilitated by the SetStatusCommand class which implements the Command interface. It allows users to mark contacts with predefined statuses. The only valid statuses are: “Uncontacted”, “Contacted”, “Rejected”, “Accepted”, “Unreachable”, and “Busy”.

The implementation is supported by the following key components:

  • SetStatusCommand - Handles the execution of the status command.
  • Status - Represents the contact status of a person as an immutable value object.
  • StatusValue - An enum defining all possible status values, and is nested in the Status class.
  • Person - Contains a person’s status along with other attributes.

Below is the class diagram showing the relationship between these components:

Status Command Class Diagram

The status command is executed through the following sequence of steps:

Status Command Sequence Diagram

Step-by-step flow:

  1. The user executes the command status 1 Contacted.
  2. The LogicManager receives the command and passes it to the AddressBookParser.
  3. The AddressBookParser identifies the command word as status and delegates the parsing of the arguments to SetStatusCommandParser.
  4. SetStatusCommandParser parses the index 1 and the string “Contacted” to create a SetStatusCommand object.
  5. The SetStatusCommand is returned to the LogicManager.
  6. The LogicManager calls the execute() method of the SetStatusCommand.
  7. The command retrieves the person at the specified index from the Model.
  8. It then creates a new Status object from the input string.
  9. A new Person object is created with the updated status.
  10. The Model is updated with the new Person object.
  11. A CommandResult is returned to the LogicManager, which is then displayed to the user.

Design Considerations

Aspect: Status Value Implementation

  • Alternative 1 (current choice): Use enum-based Status class with predefined values

    • Pros:
      • Type-safe implementation prevents invalid status values.
      • Clear indication of all available status options.
      • Easy to validate input strings.
    • Cons:
      • Adding new status values requires code changes.
      • Less flexible for user customisation.
  • Alternative 2: Use string-based status implementation

    • Pros:
      • Flexible - users could create custom status values.
      • Easier to extend without code changes.
    • Cons:
      • Less type safety.
      • More complex validation required.

Aspect: Default Status Behavior

  • Alternative 1 (current choice): Default to “Uncontacted” for empty/null input

    • Pros:
      • Consistent with the use case of tracking initial contact status.
      • Prevents null status values.
    • Cons:
      • May not be intuitive that empty input has a default value.
  • Alternative 2: Require explicit status input

    • Pros:
      • More explicit - users must state their intention.
      • Prevents accidental status changes.
    • Cons:
      • More inconvenient for salespeople when they want to reset everyone’s status (e.g. when starting a new sale).

Sharing of Contacts

The sharing of contact is required to support team collaboration workflows where managers distribute lead lists or team members share contact databases. The sharing is done via a 2-step process, exporting the contact and importing it into the app as shown by the activity diagram:

Import/Export Activity Diagram

Export Implementation Details

The export command allows users to export the address book data in JSON format to the system clipboard. It is implemented through the ExportCommand class and supported by two key interfaces:

  • ClipboardProvider - For copying data to system clipboard.
  • FileSystemProvider - For reading data from files.

The implementation is supported by these components:

  • SystemClipboardProvider - Concrete implementation for clipboard operations.
  • SystemFileSystemProvider - Concrete implementation for file system operations.
  • JsonAddressBookUtil - Handles JSON data conversion.

Below is the class diagram for the export command:

Export Command Class Diagram

The sequence diagram below shows the execution flow of the export command:

Export Command Sequence Diagram

The typical flow of operations is:

  1. User executes the export command.
  2. LogicManager calls AddressBookParser which creates an ExportCommand.
  3. The execute() method of ExportCommand is called.
  4. The command gets the address book file path from the Model.
  5. It uses the FileSystemProvider to read the content of the address book file.
  6. The content is validated to ensure it is a valid JSON representation of an address book using JsonAddressBookUtil.
  7. The content is then copied to the system clipboard using the ClipboardProvider.
  8. A CommandResult is returned and displayed to the user.

Import Implementation Details

The import feature enables salespersons to share address book data between team members via the system clipboard. This supports team collaboration workflows where managers distribute lead lists or team members share contact databases.

The import mechanism is facilitated by ImportCommand and ClipboardProvider. It uses the following key components:

  • ImportCommand — Reads JSON from clipboard and replaces the current address book
  • ClipboardProvider — Abstraction for clipboard operations (enables testing with mock clipboard)
  • JsonAddressBookUtil — Utility for JSON serialization/deserialization
  • ImportWindow — UI window for previewing import data before confirming

The following activity diagram illustrates the complete workflow of sharing contacts between team members via an external messaging application:

Import/Export Activity Diagram

The sequence diagram below shows the execution flow of the import command:

Import Command Sequence Diagram

The typical flow of operations is:

  1. User executes the import command.
  2. LogicManager calls AddressBookParser which creates an ImportCommand.
  3. The execute() method of ImportCommand is called.
  4. The command retrieves JSON data from the clipboard using ClipboardProvider.
  5. The JSON content is validated and parsed into an AddressBook object using JsonAddressBookUtil.
  6. The Model is updated with the new AddressBook, replacing the existing data.
  7. A CommandResult is returned and displayed to the user.

Design Considerations

Aspect: Export/Import Format

  • Alternative 1 (current choice): Use JSON format

    • Pros:
      • Standard format with wide tool support.
      • Human-readable.
      • Preserves data structure.
    • Cons:
      • Larger size compared to binary formats, might be too large for some systems’ clipboard to handle.
      • May expose sensitive data in readable form.
  • Alternative 2: Use binary format

    • Pros:
      • More compact.
      • Data not human-readable (better for sensitive information).
    • Cons:
      • Requires special tools to read/edit.
      • Less interoperable with other systems.

Aspect: Clipboard vs file-based import

  • Alternative 1 (current choice): Using system clipboard for data transfer

    • Pros:
      • Convenient for both backup and sharing.
      • Easier for salespeople to use, since they may not be familiar with how to locate the data files.
      • Do not have to deal with the various OS file system.
    • Cons:
      • More complex implementation, compared to just reading the file.
      • Large address books might exceed clipboard limits.
      • Data is not persisted if clipboard is cleared. However, we accept this as a trade-off because clipboard should not be used for persistence.
  • Alternative 2: Export/import via file selection dialog

    • Pros:
      • Simpler implementation. Just select and return the file.
      • Better for very large datasets.
    • Cons:
      • Less convenient for quick sharing.
      • Requires file system access.
      • Reliance on the operating system’s implementation, making it hard to ensure that it would be bug-free, and violates the constraints.

Aspect: Import replaces vs merges

  • Alternative 1 (current choice): Import replaces entire address book

    • Pros:
      • Simple and predictable behavior. No data update conflict handling is required.
      • Easier to implement within the short timeframe.
    • Cons:
      • Destructive operation loses current data if not exported first. Since sharing of contact is done to allocate the salesperson their new assignment contacts, this behaviour is acceptable.
  • Alternative 2: Merge imported contacts with existing ones

    • Pros:
      • No data is lost, allowing incremental updates.
    • Cons:
      • Complex duplicate detection and resolution. Unclear user expectations for what is considered a conflict or a new entry.

Documentation, logging, testing, configuration, dev-ops


Acknowledgements

Zhao Ruixuan

  • Used cursor to write some test cases for status and export (model: auto)
  • Used cursor to check if status and export implementation were correct (model: auto)
  • Used ChatGPT to check for spelling and formatting issues in the UG and DG (model: GPT-5)

Nihaal Manaf

  • Used Cursor to help write test cases for the find command! (model: Auto)
  • Used Cursor to help check for poor code quality in play like DRY (model: Auto)
  • Used Cursor for error checking, debugging in general(model: Auto)
  • Used Cursor to help understand the codebase architecture pattern and understand how to implement status view and tag view (model: Sonnet 4.5)
  • Used Cursor to build the tag view plane and status view plane in sidebar panel! (model: Auto)

Sean Hardjanto

  • Used Copilot to write test cases for delete and template (Claude Sonnet 4.5)
  • Used Copilot to check and write JavaDoc comments for delete and template methods (Claude Sonnet 4.5)
  • Used Copilot to scan code for areas of code to increase code quality (Claude Sonnet 4.5)
  • Used Copilot to assist with debugging of code (Claude Sonnet 4.5)
  • Used Copilot to check through UG and DG (Claude Sonnet 4.5)

Poh Anson

  • Used Copilot to help with the writing and refinement of the testcases and JavaDoc (Claude Sonnet 4.5)
  • Used Copilot to help to check for coding standards violation, poor code quality and suggestions for improvements of the code base, as well as documentations. (Claude Sonnet 4.5)
  • Used Copilot to help debug the code when I am stuck (Claude 4.5)

Appendix: Requirements

Product scope

Target user profile:

  • Salespersons who manage a large number of contacts
  • Prefer desktop apps over other types
  • Can type fast and prefer typing to mouse interactions for efficiency
  • Need to categorize leads by tags and track sales-specific statuses (e.g. Contacted, Rejected, Accepted)
  • Occasionally need to import many contacts at once (e.g. from sales manager assignments)
  • Are reasonably comfortable using CLI apps

Value proposition: An address book tailored for salespeople to manage contacts significantly faster than a typical mouse/GUI driven app, with support for bulk additions, powerful filtering by name/tag/status, status tracking to streamline outreach workflows, and templated email generation for selected contact cohorts.

Key features:

  • Add Contact: Add a single contacts in one command
  • Edit Contact: Update any field, add/remove tags, set status
  • Delete Contact: Remove contacts by index
  • List Contact: Display all contacts
  • Find Contact: Filter by name, tag, and/or status (case-insensitive, exact match)
  • Create and Copy Email Template: Generate email templates for selected tag/status cohorts
  • Bulk Import/Export of Contact: Share contacts
  • Set Status: Quickly update a contact’s status

Command format conventions:

  • Named parameters use key:value format and may appear in any order
  • Optional parameters are denoted with square brackets []
  • Repeating parameters are denoted with ...
  • Leading/trailing whitespace is trimmed for all fields
  • Each named parameter continues till the end of the line or till another parameter

Field validations for contacts:

Field Validation Rule Rationale
Name Must contain only alphanumeric characters, spaces, hyphens, apostrophes, slashes, commas, and periods. Cannot be blank or start with whitespace. Supports international names (e.g., “Mary-Jane”, “O’Brien”, “Dr. Smith”).
Phone Must contain only digits, a single optional + at the start and be at least 3 digits long. This should not be duplicated. Allows both local and international formats. Minimum length prevents some invalid inputs like “1” or “12”. We use the phone number to check for duplicate person.
However, we do not take into account the country code when comparing the phone number, that is, we treat +6598765432 and 98765432 as 2 different number. This is because we want to give users the choice to omit the country code if they are dealing with only local customer, but still not assuming that they are in a certain country.
Email Must follow standard email format, which we enforce losely by checking for @ separating local part and domain.
- Local part: alphanumeric and special characters (+, _, ., -), cannot start/end with special characters
- Domain: alphanumeric domain labels, 2 characters or longer, separated by periods. It must end with at least 2-characters domain label
- Email validation intentionally does not enforce top-level domain requirements (like .com, .org, etc.) in email addresses
Helps prevent typo of definitely invalid email address such as “name@”, “abcgmail.com” and “@gmail.com”. Also helps to accommodate rare but valid use cases where contacts may have email addresses on internal networks or custom domains that do not follow the conventional format.
Tag Only lowercase alphanumeric characters, not longer than 50 characters (no spaces or special characters). Tags should be a single word as it is used only for categorisation and not as a note. We decided upon the use of lowercase alphanumeric character only to ease the checking of duplicate tags, searching, and readability.
Status View the list below table for the list of status, and recommended meaning. Defaults to “Uncontacted” if not specified. Helps to track contacts for the sales workflow. Case-insensitive matching improves user experience.

Valid contact statuses:

  • Uncontacted: Contact has not been contacted yet (default)
  • Contacted: Contact has been contacted
  • Rejected: Contact has rejected the sale
  • Accepted: Contact has accepted the sale
  • Unreachable: Contact could not be reached
  • Busy: Contact is busy and should be contacted later

User stories

Priorities: Essential (must have) - * * *, Typical (nice to have) - * *, Novel (unlikely to have) - *

Priority As a … I want to … So that I can…
* * * salesperson add contacts see their details in the future
* * * salesperson delete contacts don’t over clutter my contact book
* * salesperson delete multiple contacts ensure that PDPA retention limitation is adhered to
* * * careless salesperson edit contact details ensure the data is accurate
* * * salesperson search by name easily find contacts by name due to the large number of contacts
* * salesperson search by tags easily find contacts by tags
* * salesperson search by status easily find contacts by their status
* * * salesperson list all contacts know what contacts I have saved
* * * forgetful user have my edits autosaved data won’t be lost if I forget to save it
* * salesperson team IC export and share the contacts I have with others easily don’t need my team to use each others’ accounts
* * salesperson team IC import contacts shared by others quickly add contacts provided by my team
* * salesperson add tags to contacts categorise them for filtering
* * salesperson add multiple tags to each contact better categorise contacts with different characteristics
* * salesperson set a status for each contact track which contacts have been contacted, accepted, rejected, etc.
* salesperson create and edit email templates for different contact types save time from writing up the same outreach materials over and over again
* salesperson copy a template message to clipboard quickly paste it into my email application
* salesperson mark clients as rejected avoid wasting time by contacting them again
* busy salesperson mark clients based on how receptive they are focus my limited time on those likely to buy the product

Use cases

Use case: UC01 - Add new contact

System: Contact Management System (CMS)

Actor: Salesperson

Guarantees:

  • Contact is created only if all required fields are valid.
  • On validation error, no contacts are added.

MSS:

  1. Salesperson chooses to add a new contact.
  2. Salesperson enters the add command with contact details.
  3. Salesperson submits the command.
  4. CMS validates the details.
  5. CMS creates the contact and displays a confirmation message.
    Use case ends.

Extensions:

4a. CMS detects an error in the entered data.
4a1. CMS indicates an error has happened.
Use case resumes from step 2.

Use case: UC02 - Import address book from clipboard

System: Contact Management System (CMS)

Actor: Salesperson

Guarantees:

  • On success, the address book on disk is replaced by the imported data and the UI reflects the new data.
  • No changes are made to the current address book if the clipboard is empty or contains invalid JSON.

MSS:

  1. Salesperson copies the address book JSON to the system clipboard.
  2. Salesperson issues the import command.
  3. CMS reads and validate the clipboard content. Use case ends.

Extensions:

3a. Clipboard is empty.
3a1. CMS displays: “Clipboard does not contain any text to import”.
Use case ends.

3b. Clipboard content is not valid address book JSON or fails validation.
3b1. CMS displays: “Failed to import: Clipboard does not contain valid address book JSON.”.
Use case ends.

Use case: UC03 - List all contacts

System: Contact Management System (CMS)

Actor: Salesperson

Guarantees:

  • Listing does not modify any data.
  • The latest saved state of contacts is displayed.

MSS:

  1. Salesperson chooses to view all contacts.
  2. Salesperson enters the list command.
  3. CMS displays all contacts.
    Use case ends.

Extensions:

3a. The contact list is empty.
3a1. CMS indicates that no contacts are found.
Use case ends.

Use case: UC04 - Find customers by various criteria

System: Contact Management System (CMS)

Actor: Salesperson

Guarantees:

  • Search does not modify customer data.
  • Tag view and status view panels update to reflect active filters.

MSS:

  1. Salesperson chooses to find customers by one or more criteria (name, tag, status, phone, email).
  2. Salesperson enters the find command with specified search criteria.
  3. CMS searches for customers matching ALL specified criteria (AND logic between different types, OR logic within same type).
  4. CMS displays the matching customers and updates the tag view and status view panels to show active filters.
    Use case ends.

Extensions:

2a. Invalid search criteria format provided.
2a1. CMS indicates invalid command format and shows usage instructions.
Use case ends.

3a. No customers match the search criteria.
3a1. CMS displays an empty list while keeping the filters visible in tag view and status view to show search intent.
Use case ends.

Use case: UC05 - Find contact by status

System: Contact Management System (CMS)

Actor: Salesperson

Guarantees:

  • Filtering does not modify contact data.

MSS:

  1. Salesperson chooses to find contacts by status.
  2. Salesperson specifies the status.
  3. CMS searches for contacts with the specified status.
  4. CMS displays the matching contacts.
    Use case ends.

Extensions:

2a. The specified status does not exist.
2a1. CMS indicates that an error has happened.
Use case ends.

4a. No contacts match the specified status.
4a1. CMS indicates that no contacts are found.
Use case ends.

Use case: UC06 - Open and edit email template

System: Contact Management System (CMS)

Actor: Salesperson

Guarantees:

  • Opening a template does not modify any data.
  • Template is saved only when the save command is explicitly issued.
  • If the template view is switched without saving, no changes are will be saved.
  • Blank or whitespace-only content is automatically replaced with the default template.

MSS:

  1. Salesperson chooses to open an email template for a specific status.
  2. Salesperson enters the template command with the status.
  3. CMS retrieves the template for the specified status.
  4. CMS displays the template in an editable view.
  5. Salesperson edits the template content.
  6. Salesperson issues the save command.
  7. CMS saves the updated template.
    Use case ends.

Extensions:

*a. At any time, Salesperson enters a different command (e.g. list, find).
*a1. CMS switches back to the main view and discards any unsaved edits.
Use case ends.

2a. The specified status is invalid.
2a1. CMS indicates that an error has happened.
Use case ends.

3a. No template exists for the specified status, or the template file contains only blank/whitespace content.
3a1. CMS displays the default template for that status.
Use case resumes from step 4.

6a. Salesperson saves blank or whitespace-only content.
6a1. CMS detects the blank content and saves the default template instead.
6a2. CMS displays: “Detected empty template as input, saving as the default template instead.”
Use case ends.

Use case: UC07 - Copy email template to clipboard

System: Contact Management System (CMS)

Actor: Salesperson

Guarantees:

  • Copying a template does not modify any data.
  • The template content is placed on the system clipboard.
  • If the template file is blank or contains only whitespace, the default template is copied instead.

MSS:

  1. Salesperson chooses to copy an email template for a specific status.
  2. Salesperson enters the copy template command with the status.
  3. CMS retrieves the template for the specified status.
  4. CMS copies the template content to the clipboard.
  5. CMS displays a confirmation message.
    Use case ends.

Extensions:

2a. The specified status is invalid.
2a1. CMS indicates that an error has happened.
Use case ends.

3a. No template exists for the specified status, or the template file contains only blank/whitespace content.
3a1. CMS retrieves the default template for that status and copies it to the clipboard.
Use case resumes from step 5.

Use case: UC08 - Edit contact

System: Contact Management System (CMS)

Actor: Salesperson

Guarantees:

  • Only the specified fields are updated, the other fields remain unchanged.
  • On validation error, no changes are applied.

MSS:

  1. Salesperson chooses to edit a contact’s information.
  2. Salesperson specifies the contact ID and fields to edit.
  3. CMS validates the updated details.
  4. CMS updates the contact information.
    Use case ends.

Extensions:

2a. The specified contact ID does not exist.
2a1. CMS indicates that an error has happened.
Use case resumes from step 2.

3a. CMS detects an error in the entered data.
3a1. CMS indicates that an error has happened.
Use case resumes from step 2.

Use case: UC09 - Delete contact(s)

System: Contact Management System (CMS)

Actor: Salesperson

Guarantees:

  • Deletion removes only the specified contact(s) without modifying other data.
  • All specified contacts must be valid or none will be deleted.
  • Deletion is irreversible once confirmed.
  • On failure, no deletion occurs.
  • All specified indices must be valid; if any index is invalid, no deletions are performed.

MSS:

  1. Salesperson chooses to delete one or more contacts.
  2. Salesperson specifies the contact ID(s) to delete.
  3. CMS validates all contact IDs.
  4. CMS deletes the contact(s) and displays a confirmation.
    Use case ends.

Extensions:

3a. One or more of the given contact IDs are invalid.
3a1. CMS displays which contact ID(s) are invalid and indicates that no deletions were performed.
Use case resumes at step 2.

Use case: UC10 - Set contact status

System: Contact Management System (CMS)

Actor: Salesperson

Guarantees:

  • If an invalid status is provided, no changes are made.

MSS:

  1. Salesperson chooses to update a contact’s status.
  2. Salesperson specifies the contact ID and new status.
  3. CMS validates the contact ID and status.
  4. CMS updates the contact’s status and displays a confirmation message.
    Use case ends.

Extensions:

2a. No status is specified.
2a1. CMS defaults the status of the contact to be ‘Uncontacted”.
Use case resumes at step 4.

3a. The specified status is invalid.
3a1. CMS indicates that an error has happened.
Use case ends.

3b. The specified contact ID does not exist.
3b1. CMS indicates that an error has happened.
Use case ends.

Use case: UC11 - Export Contacts

System: Contact Management System (CMS)

Actor: Salesperson

Guarantees:

  • Export copies the exact state of the address book to clipboard as JSON.
  • All contact data is preserved during export.
  • The operation does not modify any existing data.

MSS:

  1. Salesperson chooses to export contacts.
  2. Salesperson enters the export command or presses F8.
  3. CMS reads the address book data.
  4. CMS converts the data to JSON format.
  5. CMS copies the JSON to the system clipboard.
  6. CMS displays a success message.
    Use case ends.

Extensions:

3a. CMS cannot read the address book file.
3a1. CMS shows an error message.
Use case ends.

5a. System clipboard is unavailable.
5a1. CMS shows an error message.
Use case ends.

Use case: UC12 - Import Contacts

System: Contact Management System (CMS)

Actor: Salesperson

Guarantees:

  • Import either succeeds completely or fails without modifying existing data.

MSS:

  1. Salesperson chooses to import contacts.
  2. Salesperson enters the import command.
  3. CMS retrieves JSON data from the system clipboard.
  4. CMS validates the JSON format and contact data.
  5. CMS overwrites the existing address book with the imported data.
  6. CMS displays a success message.
    Use case ends.

Extensions: 1a. Salesperson presses F7 or clicks the import button under File.
1a1. CMS shows an import contact preview window.
1a2. User clicks paste JSON button.
Use case resumes at step 3.

3a. Clipboard is empty.
3a1. CMS shows an error message.
Use case ends.

3b. Cannot access system clipboard.
3b1. CMS shows an error message.
Use case ends.

4a. The JSON data is invalid or malformed.
4a1. CMS shows an error message describing the issue.
4a2. No changes are made to the existing data.
Use case ends.

Non-Functional Requirements

  1. Should work on any mainstream OS as long as it has Java 17 installed (Java 17 JDK+FX Azul distribution on macOS).
  2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage, especially in things like returning search results or filtering by label.
  3. A user with above average typing speed (>60 WPM) 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.
  4. The commands should follow consistent patterns to reduce confusion and make it easier to learn them.
  5. User data should be automatically saved after commands such that user data is not lost even after unexpected shutdowns.
  6. The program should be able to be self-contained as a single JAR/ZIP file that should work without requiring any installer.
  7. The GUI should work well for standard screen resolutions 1920x1080 and higher, and, for screen scales 100% and 125%, meaning that no clipping or obvious bugs show in the GUI.
  8. The GUI should be usable (i.e. all functions can be used even if the user experience is not optimal) for resolutions 1280x720 and higher, and, for screen scales 150%.
  9. There should not be a server component. All data should be stored on the user’s local machine.

Glossary

  • Address Book: The core domain model of the application. Represents the collection of all contacts and provides operations for managing them.
  • API (Application Programming Interface): The set of public methods and interfaces that define how components interact with each other. Each major component (UI, Logic, Model, Storage) defines its API through an interface.
  • Case-insensitive: String matching that ignores letter casing (e.g., “John” matches “joHn”, “JOHN”).
  • CLI (Command-Line Interface): Text-based user interface where users type commands. Parsed by AddressBookParser and individual XYZCommandParser classes.
  • Clipboard: System clipboard abstracted through the ClipboardProvider interface. Allows copying templates and address book data for external use. Production code uses SystemClipboardProvider, tests use stubs.
  • ClipboardProvider: An abstraction/interface for operations that interact with the system clipboard (e.g. for copying contacts information or templates programmatically).
  • Command: An executable object representing a user action. All commands extend the abstract Command class and implement execute(Model). Examples: AddCommand, DeleteCommand, TemplateCommand.
  • CommandBox: The text input area in the UI where users enter commands.
  • CommandResult: Encapsulates the outcome of command execution. Contains success/error message and flags indicating UI actions (e.g., isShowHelp, isExit, isShowTemplate).
  • Contact: Refers to a Person object in the domain model. Used interchangeably with “Person” in documentation.
  • Email Template: Persistent text content associated with a Status enum value. Stored in JSON files by TemplateStorage and managed via TemplateCommand.
  • Export/Import: Features to serialise/deserialise the entire AddressBook to/from clipboard as a JSON formatted string. Uses JsonAddressBookUtil for conversion. Enables easier data sharing between users.
  • FileSystemProvider: Interface that abstracts file read/write operations from the file system for portability and testing.
  • Filter: Applying a Predicate<Person> to the filteredPersons observable list in Model. Updates the UI to show only matching contacts.
  • GUI (Graphical User Interface): The JavaFX-based visual interface. Implemented in the UI component with FXML layouts and corresponding controller classes.
  • ImportWindow: A separate UI window dedicated to importing customer data from the clipboard, allowing preview or validation before adding contacts.
  • Index: A 1-based position reference used in commands to identify contacts in the displayed list. Internally converted to 0-based for List operations. Represented by the Index class.
  • JsonAddressBookUtil: A utility class that handles conversion (serialization/deserialization) between Address Book data structures and JSON format.
  • LogicManager: The concrete implementation of the application’s Logic component. Orchestrates command parsing, command execution, and data flow between UI and Model.
  • Mainstream OS: Windows, Linux, MacOS - target platforms for the application.
  • MainWindow: The primary application window in the UI, containing CommandBox, PersonListPanel, SidebarPanel, and other subcomponents.
  • Model: The component responsible for holding application data in memory. Manages AddressBook, UserPrefs, and filtered lists. Exposes data through ObservableList for reactive UI updates.
  • ModelManager: The main implementation of the Model interface. Manages, updates, and exposes application data in-memory and propagates property changes to the UI.
  • ObservableList: A JavaFX collection that notifies listeners about changes (additions, removals, updates). Used to keep the UI view synchronized with app data in real time.
  • ObjectProperty: A JavaFX property type that holds and notifies changes to a single object, supporting binding and listeners for UI updates.
  • Observer Pattern: Design pattern used to keep UI synchronized with Model. JavaFX ObservableList and ObjectProperty notify listeners (UI components) when data changes.
  • Parameter: Command argument specified with a prefix (e.g., n:Mary Jane, p:91234567). Parsed by ArgumentTokenizer which splits input into ArgumentMultimap.
  • Parser: Class responsible for converting user input strings into Command objects. All parsers implement the Parser interface.
  • PDPA (Personal Data Protection Act): Singapore’s data protection regulation. Application supports compliance through bulk deletion and data export features.
  • PersonListPanel: The panel or list view in the UI that displays all persons/contacts matching the current list or filter.
  • Predicate: A functional interface representing a boolean-valued function. Used extensively for filtering (e.g. PersonMatchesKeywordsPredicate, NameContainsKeywordsPredicate).
  • Prefix: A Prefix object (e.g., PREFIX_NAME, PREFIX_PHONE) used by parsers to identify parameter types. Defined in CliSyntax.
  • ResultDisplay: A UI box or output panel that shows feedback, messages, and results to users following command execution.
  • SidebarPanel: A UI panel that contains and displays active filters; houses the StatusViewPanel and TagsViewPanel.
  • Status: An enum-like class representing contact lifecycle states (Contacted, Rejected, Accepted, Unreachable, Busy, Uncontacted). Used for filtering and template association.
  • StatusViewPanel: UI component that displays the list of currently active status filters applied (e.g., via find command).
  • StatusViewState: Model object representing the current state of selected or displayed status filters for UI update.
  • Storage: The component handling data persistence. Implements both AddressBookStorage and UserPrefStorage interfaces. Uses JSON format.
  • SystemClipboardProvider: The production implementation of ClipboardProvider that interacts with the real system clipboard on the user’s OS.
  • SystemFileSystemProvider: The production implementation of FileSystemProvider that uses the local file system for file operations.
  • TagsViewPanel: UI component that displays the list of currently active tag filters applied (e.g., via find command).
  • TagsViewState: Model object representing the current state of selected or displayed tag filters for UI update.
  • Template Storage: Subsystem for persisting email templates. Uses TemplateStorage interface with file-based implementation (TemplateStorageManager). Templates stored as individual files per status.
  • TemplateViewPanel: UI component that allows users to create, edit, and view email templates corresponding to customer statuses.
  • TemplateViewState: Model object representing which template is being edited, along with its content, for template editor synchronization.
  • UI Component: JavaFX-based view layer. Inherits from UiPart base class. FXML files in resources/view define layouts, Java classes handle logic.
  • UiPart: Abstract Java class that defines common logic for UI components/parts (JavaFX controls) in the app. All custom UI views inherit from this base class
  • UniquePersonList: Internal data structure in AddressBook that ensures no duplicate persons. Duplicates determined by Person#isSamePerson() method.
  • UX: User Experience. How the user feels when interacting with the system.
  • Validation: Input checking performed by parsers and domain objects. For example, Phone validates format, Email validates structure. Throws ParseException or IllegalArgumentException on invalid input.

Appendix: Instructions for manual testing

Given below are instructions to test the app manually.

:information_source: Note: These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.

Launch and shutdown

  1. Initial launch

    1. Download the jar file and copy into an empty folder

    2. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.

  2. Saving window preferences

    1. Resize the window to an optimum size. Move the window to a different location. Close the window.

    2. Re-launch the app by double-clicking the jar file.
      Expected: The most recent window size and location is retained.

Deleting a person

  1. Deleting a person while all persons are being shown

    1. Prerequisites: List all persons using the list command. Multiple persons in the list.

    2. Test case: delete 1
      Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.

    3. Test case: delete 0
      Expected: No person is deleted. Error message indicating invalid command format is shown (index must be a non-zero unsigned integer).

    4. Test case: delete -1
      Expected: No person is deleted. Error message indicating invalid command format is shown (index must be a non-zero unsigned integer).

    5. Test case: delete 1 2 3 (assuming only 2 contacts exist)
      Expected: No persons are deleted. Error message shows “Invalid index(es) detected: 3” indicating which index is invalid.

    6. Test case: delete 1 -5 3 (mixing valid and negative indices)
      Expected: No person is deleted. Error message indicating invalid command format is shown (index must be a non-zero unsigned integer).

    7. Other incorrect delete commands to try: delete, delete x, delete 1 99 (where x is larger than the list size)
      Expected: For delete and delete x: Invalid command format. For delete 1 99: Error message displays the specific invalid indices.

Adding a contact

  1. Adding a contact with all fields

    1. Test case: add n:John Doe p:98765432 e:johnd@example.com a:311, Clementi Ave 2, #02-25 s:Contacted t:friend t:colleague
      Expected: New contact “John Doe” is added to the list. Success message shown with contact details. The contact appears in the list with status “Contacted” and tags “friend” and “colleague”.

    2. Test case: add n:Jane Smith p:87654321 e:janes@example.com a:123 Main St s:Uncontacted
      Expected: New contact “Jane Smith” is added with status “Uncontacted” and no tags. Success message displayed.

  2. Adding a contact with minimal required fields

    1. Test case: add n:Bob Lee p:91234567 e:bob@example.com a:456 Side St
      Expected: Contact is added successfully without status or tags. Default status may be applied.
  3. Adding a contact with invalid or missing fields

    1. Test case: add n:Invalid p:invalid_phone e:test@example.com a:Some Address
      Expected: Error message indicating invalid phone number format. No contact is added.

    2. Test case: add n:NoPhone e:test@example.com a:Some Address
      Expected: Error message about missing required field (phone). No contact is added.

    3. Test case: add n:Alice p:12345678 e:invalidemail a:Some Address
      Expected: Error message indicating invalid email format. No contact is added.

    4. Test case: add p:91234567 e:test@example.com a:Some Address
      Expected: Error message about missing name. No contact is added.

  4. Adding a duplicate contact

    1. Test case: add n:Jane Doe p:98765432 e:different@example.com a:Different Address
      Expected: Error message “This person already exists in the address book”. No new contact is added. (Note: Duplicate detection is based on phone only)

Importing address book from clipboard

  1. Importing valid address book JSON

    1. Copy the following valid JSON to clipboard:

      {
        "persons": [
          {
            "name": "Alice Tan",
            "phone": "91234567",
            "email": "alice@example.com",
            "address": "123 Street",
            "status": "Contacted",
            "tagged": ["friend"]
          }
        ]
      }
      
    2. Test case: import
      Expected: Address book is replaced with the imported data. Success message displayed. UI shows “Alice Tan” contact. All previous contacts are replaced.

  2. Importing with invalid clipboard

    1. Prerequisites: Ensure clipboard is empty or contains does not contain text.

    2. Test case: import
      Expected: Error message “Clipboard does not contain any text to import”. Address book remains unchanged.

  3. Importing with invalid JSON

    1. Copy invalid JSON to clipboard (e.g. {invalid json}).

    2. Test case: import
      Expected: Error message “Failed to import: Clipboard does not contain valid address book JSON.”. Address book remains unchanged.

    3. Copy non-address-book JSON to clipboard (e.g. {"name": "test"}).

    4. Test case: import
      Expected: Error message “Failed to import: Clipboard does not contain valid address book JSON.”. Address book remains unchanged.

  4. Importing with invalid data

    1. Copy JSON with missing required fields:

      {
        "persons": [
          {
            "name": "Bob",
            "phone": "invalid",
            "email": "bob@gmail.com"
          }
        ]
      }
      
    2. Test case: import
      Expected: Error message “Failed to import: Clipboard does not contain valid address book JSON.”. Address book remains unchanged.

Finding customers

  1. Finding customers with various criteria such as name, tag, status, phone number and email!

    1. Prerequisites: List all customers using the list command. Multiple customers in the list with different attributes.

    2. Test case: find n:John
      Expected: Customers with “John” in their name are displayed. Tag view and status view remain unchanged.

    3. Test case: find s:Contacted
      Expected: Customers with “Contacted” status are displayed. Status view panel updates to show “Contacted” as active filter.

    4. Test case: find t:friends s:Contacted
      Expected: Customers with “friends” tag AND “Contacted” status are displayed. Both tag view and status view show active filters.

    5. Test case: find
      Expected: Error message indicating invalid command format.

    6. Other test cases to try: find p:9876, find e:example.com, find n:alex david (multiple keywords), find s:Invalid (invalid status)
      Expected: Error thrown or empty list!

Appendix: Planned Enhancements

Better Phone Number Validation

Because the app is platform‑independent, we cannot reliably determine the user’s region to infer a default country code. Additionally, telephone numbers vary widely from country to country, with varying length of country codes and local numbers. Hence, implementing robust parsing without third‑party libraries is out of scope for this release. For example, +355 is considered as a valid number by our application, but in reality, it is invalid as all 3 digits are used for the country code.

We plan to be able to implement a better phone validation customised to the country code, if provided. Additionally, we plan to infer a reasonable default country code from the device locale or user settings, so that we could better check for duplicate numbers by removing the country code prefix.