diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index a3bbe31aa7f..37701780916 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -161,6 +161,18 @@ Classes used by multiple components are in the `seedu.addressbook.commons` packa This section describes some noteworthy details on how certain features are implemented. +### View Specific Person feature +Step 1: The user launches the application for the first time. The `SampleAddressBook` will be initialised. + +Step 2: The user executes `addclient n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` to add a new Client. + +Step 3: The user executes a `view 1` command to view the 1st person in the address book. The user’s command is parsed by `ViewCommandParser` which extracts the target index. The `ViewCommand` class is instantiated with the extracted index. `ViewCommand` class interacts with `Model#FilteredPersonList` to verify the validity of the index and retrieve the corresponding person’s details. The command execution would be encapsulated as a `CommandResult` object that is then returned back from `Logic`. + +**Note:** If the index given is more than the size of the list or when the index given is 0, `ViewCommand` will not call `Model#view(Person personToView)`. Instead, a `MESSAGE_INVALID_PERSON_DISPLAYED_INDEX` exception will be thrown. The Main Window display continue displaying the `PersonListPanel` UI instead of the `ViewWindow` UI + +The following sequence diagram shows how the View Command works: +ViewSequenceDiagram + ### \[Proposed\] Undo/redo feature #### Proposed Implementation @@ -545,6 +557,19 @@ testers are expected to do more *exploratory* testing. 1. _{ more test cases …​ }_ +### Viewing a person + +1. Viewing a person while all persons are being shown + + 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. + + 1. Test case: `view 0`
+ Expected: Entire list remains displayed. Error details shown in status message. Status bar remains the same. + 1. Other incorrect view commands to try: `view`, `view x` (where x is larger than the list size, or x is a negative index)
+ Expected: Similar to previous. + 1. Test case: `view 1`, `view x` (where x is an integer within the size of the list)
+ Expected: The full details of the first person is displayed. Success message: `Viewed Person Successfully` + ### Saving data 1. Dealing with missing/corrupted data files diff --git a/docs/diagrams/UiClassDiagram.png b/docs/diagrams/UiClassDiagram.png index f104adb37c9..d46fdc84bd7 100644 Binary files a/docs/diagrams/UiClassDiagram.png and b/docs/diagrams/UiClassDiagram.png differ diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 95473d5aa19..58d2b005320 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -1,5 +1,6 @@ @startuml !include style.puml +!pragma layout smetana skinparam arrowThickness 1.1 skinparam arrowColor UI_COLOR_T4 skinparam classBackgroundColor UI_COLOR @@ -15,6 +16,7 @@ Class PersonListPanel Class PersonCard Class StatusBarFooter Class CommandBox +Class ViewWindow } package Model <> { @@ -35,6 +37,7 @@ MainWindow *-down-> "1" ResultDisplay MainWindow *-down-> "1" PersonListPanel MainWindow *-down-> "1" StatusBarFooter MainWindow --> "0..1" HelpWindow +MainWindow --> "0..1" ViewWindow PersonListPanel -down-> "*" PersonCard @@ -46,6 +49,7 @@ PersonListPanel --|> UiPart PersonCard --|> UiPart StatusBarFooter --|> UiPart HelpWindow --|> UiPart +ViewWindow --|> UiPart PersonCard ..> Model UiManager -right-> Logic diff --git a/docs/diagrams/ViewSequenceDiagram.png b/docs/diagrams/ViewSequenceDiagram.png new file mode 100644 index 00000000000..93dfaf4d045 Binary files /dev/null and b/docs/diagrams/ViewSequenceDiagram.png differ diff --git a/docs/diagrams/ViewSequenceDiagram.puml b/docs/diagrams/ViewSequenceDiagram.puml new file mode 100644 index 00000000000..b36ae2c63a3 --- /dev/null +++ b/docs/diagrams/ViewSequenceDiagram.puml @@ -0,0 +1,70 @@ +@startuml +!include style.puml +skinparam ArrowFontStyle plain + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":ViewCommandParser" as ViewCommandParser LOGIC_COLOR +participant "d:ViewCommand" as ViewCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("view 1") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("view 1") +activate AddressBookParser + +create ViewCommandParser +AddressBookParser -> ViewCommandParser +activate ViewCommandParser + +ViewCommandParser --> AddressBookParser +deactivate ViewCommandParser + +AddressBookParser -> ViewCommandParser : parse("1") +activate ViewCommandParser + +create ViewCommand +ViewCommandParser -> ViewCommand +activate ViewCommand + +ViewCommand --> ViewCommand : v +deactivate ViewCommand + +ViewCommandParser --> AddressBookParser : v +deactivate ViewCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +ViewCommandParser -[hidden]-> AddressBookParser +destroy ViewCommandParser + +AddressBookParser --> LogicManager : v +deactivate AddressBookParser + +LogicManager -> ViewCommand : execute() +activate ViewCommand + +ViewCommand -> Model : view(person) +activate Model + +Model --> ViewCommand +deactivate Model + +create CommandResult +ViewCommand -> CommandResult +activate CommandResult + +CommandResult --> ViewCommand +deactivate CommandResult + +ViewCommand --> LogicManager : result +deactivate ViewCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/src/main/java/seedu/address/logic/commands/ConvertClientToLeadCommand.java b/src/main/java/seedu/address/logic/commands/ConvertClientToLeadCommand.java deleted file mode 100644 index 1d559d1b6ae..00000000000 --- a/src/main/java/seedu/address/logic/commands/ConvertClientToLeadCommand.java +++ /dev/null @@ -1,92 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.Messages; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Address; -import seedu.address.model.person.Client; -import seedu.address.model.person.Email; -import seedu.address.model.person.Lead; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Converts a Client to a Lead in the address book. - */ -public class ConvertClientToLeadCommand extends Command { - public static final String COMMAND_WORD = "converttolead"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Converts a client to a lead by the index " - + "number used in the displayed person list. \n" - + "Parameters: INDEX (must be a positive integer)\n" - + "Example: " + COMMAND_WORD + " 1"; - - public static final String MESSAGE_CONVERT_SUCCESS = "Converted Client to Lead: %1$s"; - - private final Index index; - - /** - * Constructor for convertClientToLead. - */ - public ConvertClientToLeadCommand(Index index) { - requireNonNull(index); - this.index = index; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); - - if (index.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - Person personToConvert = lastShownList.get(index.getZeroBased()); - - if (!(personToConvert instanceof Client)) { - throw new CommandException("The person at the specified index is not a Client."); - } - - Name name = personToConvert.getName(); - Phone phone = personToConvert.getPhone(); - Email email = personToConvert.getEmail(); - Address address = personToConvert.getAddress(); - Set tags = new HashSet(); - tags.remove(new Tag("Client")); - tags.add(new Tag("Lead")); - // TODO: Add more fields from client to lead - - - Lead convertedLead = new Lead(name, phone, email, address, tags); - - model.setPerson(personToConvert, convertedLead); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_CONVERT_SUCCESS, Messages.format(convertedLead))); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof ConvertClientToLeadCommand)) { - return false; - } - - ConvertClientToLeadCommand otherConvertCommand = (ConvertClientToLeadCommand) other; - return index.equals(otherConvertCommand.index); - } -} - diff --git a/src/main/java/seedu/address/logic/commands/ConvertLeadToClientCommand.java b/src/main/java/seedu/address/logic/commands/ConvertLeadToClientCommand.java deleted file mode 100644 index dcf56ec1e65..00000000000 --- a/src/main/java/seedu/address/logic/commands/ConvertLeadToClientCommand.java +++ /dev/null @@ -1,91 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.Messages; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Address; -import seedu.address.model.person.Client; -import seedu.address.model.person.Email; -import seedu.address.model.person.Lead; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Converts a Lead to a Client in the address book. - */ -public class ConvertLeadToClientCommand extends Command { - public static final String COMMAND_WORD = "converttoclient"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Converts a lead to a client by the index " - + "number used in the displayed person list. \n" - + "Parameters: INDEX (must be a positive integer)\n" - + "Example: " + COMMAND_WORD + " 1"; - - public static final String MESSAGE_CONVERT_SUCCESS = "Converted Lead to Client: %1$s"; - - private final Index index; - - /** - * Constructor for ConvertLeadToClientCommand. - */ - public ConvertLeadToClientCommand(Index index) { - requireNonNull(index); - this.index = index; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); - - if (index.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - Person personToConvert = lastShownList.get(index.getZeroBased()); - - if (!(personToConvert instanceof Lead)) { - throw new CommandException("The person at the specified index is not a Lead."); - } - - Name name = personToConvert.getName(); - Phone phone = personToConvert.getPhone(); - Email email = personToConvert.getEmail(); - Address address = personToConvert.getAddress(); - Set tags = new HashSet<>(); - tags.remove(new Tag("Lead")); - tags.add(new Tag("Client")); - // TODO: Add more fields from lead to client - - Client convertedClient = new Client(name, phone, email, address, tags); - - model.setPerson(personToConvert, convertedClient); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_CONVERT_SUCCESS, Messages.format(convertedClient))); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof ConvertLeadToClientCommand)) { - return false; - } - - ConvertLeadToClientCommand otherConvertCommand = (ConvertLeadToClientCommand) other; - return index.equals(otherConvertCommand.index); - } -} - diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index bd4ce66cb77..f8d667bedf5 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -12,8 +12,6 @@ import seedu.address.logic.commands.AddLeadCommand; import seedu.address.logic.commands.ClearCommand; import seedu.address.logic.commands.Command; -import seedu.address.logic.commands.ConvertClientToLeadCommand; -import seedu.address.logic.commands.ConvertLeadToClientCommand; import seedu.address.logic.commands.DeleteCommand; import seedu.address.logic.commands.EditCommand; import seedu.address.logic.commands.ExitCommand; @@ -91,14 +89,10 @@ public Command parseCommand(String userInput) throws ParseException { case ListLeadCommand.COMMAND_WORD: return new ListLeadCommand(); - case ConvertLeadToClientCommand.COMMAND_WORD: - return new ConvertLeadToClientCommandParser().parse(arguments); - - case ConvertClientToLeadCommand.COMMAND_WORD: - return new ConvertClientToLeadCommandParser().parse(arguments); case ViewCommand.COMMAND_WORD: return new ViewCommandParser().parse(arguments); + default: logger.finer("This user input caused a ParseException: " + userInput); throw new ParseException(MESSAGE_UNKNOWN_COMMAND); diff --git a/src/main/java/seedu/address/logic/parser/ConvertClientToLeadCommandParser.java b/src/main/java/seedu/address/logic/parser/ConvertClientToLeadCommandParser.java deleted file mode 100644 index ee4db5b6a8e..00000000000 --- a/src/main/java/seedu/address/logic/parser/ConvertClientToLeadCommandParser.java +++ /dev/null @@ -1,36 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.ConvertClientToLeadCommand; -import seedu.address.logic.parser.exceptions.ParseException; - -/** - * Parses input arguments and creates a new FindCommand object - */ -public class ConvertClientToLeadCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the FindCommand - * and returns a FindCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - - public ConvertClientToLeadCommand parse(String args) throws ParseException { - String trimmedArgs = args.trim(); - if (trimmedArgs.isEmpty()) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, ConvertClientToLeadCommand.MESSAGE_USAGE)); - } - - try { - Index index = ParserUtil.parseIndex(trimmedArgs); - return new ConvertClientToLeadCommand(index); - } catch (ParseException pe) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, ConvertClientToLeadCommand.MESSAGE_USAGE), pe); - } - } - -} diff --git a/src/main/java/seedu/address/logic/parser/ConvertLeadToClientCommandParser.java b/src/main/java/seedu/address/logic/parser/ConvertLeadToClientCommandParser.java deleted file mode 100644 index a56fc59e7cc..00000000000 --- a/src/main/java/seedu/address/logic/parser/ConvertLeadToClientCommandParser.java +++ /dev/null @@ -1,36 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.ConvertLeadToClientCommand; -import seedu.address.logic.parser.exceptions.ParseException; - -/** - * Parses input arguments and creates a new FindCommand object - */ -public class ConvertLeadToClientCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the FindCommand - * and returns a FindCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - - public ConvertLeadToClientCommand parse(String args) throws ParseException { - String trimmedArgs = args.trim(); - if (trimmedArgs.isEmpty()) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, ConvertLeadToClientCommand.MESSAGE_USAGE)); - } - - try { - Index index = ParserUtil.parseIndex(trimmedArgs); - return new ConvertLeadToClientCommand(index); - } catch (ParseException pe) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, ConvertLeadToClientCommand.MESSAGE_USAGE), pe); - } - } - -} diff --git a/src/test/java/seedu/address/logic/commands/ConvertClientToLeadCommandTest.java b/src/test/java/seedu/address/logic/commands/ConvertClientToLeadCommandTest.java deleted file mode 100644 index 693f739c9ba..00000000000 --- a/src/test/java/seedu/address/logic/commands/ConvertClientToLeadCommandTest.java +++ /dev/null @@ -1,41 +0,0 @@ -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; - -import org.junit.jupiter.api.Test; - -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Client; -import seedu.address.model.person.Lead; -import seedu.address.testutil.TypicalPersons; - -public class ConvertClientToLeadCommandTest { - private Model model = new ModelManager(TypicalPersons.getTypicalAddressBook(), new UserPrefs()); - private Model expectedModel = new ModelManager(TypicalPersons.getTypicalAddressBook(), new UserPrefs()); - - @Test - public void execute_validClientIndex_success() { - Client clientToConvert = TypicalPersons.getTypicalClients().get(0); // assuming you have a list of clients - ConvertClientToLeadCommand command = new ConvertClientToLeadCommand(clientToConvert.getIndex()); - - String expectedMessage = String.format(ConvertClientToLeadCommand.MESSAGE_CONVERT_SUCCESS, clientToConvert); - expectedModel.deletePerson(clientToConvert); - expectedModel.addPerson(new Lead(clientToConvert)); // Assuming a constructor for Lead is available - - assertCommandSuccess(command, model, expectedMessage, expectedModel); - } - - @Test - public void execute_invalidClientIndex_failure() { - int outOfBoundIndex = model.getFilteredPersonList().size() + 1; - ConvertClientToLeadCommand command = new ConvertClientToLeadCommand(Index.fromOneBased(outOfBoundIndex)); - - assertCommandFailure(command, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - // Add more test cases for edge cases, if necessary -} - diff --git a/src/test/java/seedu/address/logic/commands/ConvertLeadToClientCommandTest.java b/src/test/java/seedu/address/logic/commands/ConvertLeadToClientCommandTest.java deleted file mode 100644 index 8f738d687bf..00000000000 --- a/src/test/java/seedu/address/logic/commands/ConvertLeadToClientCommandTest.java +++ /dev/null @@ -1,41 +0,0 @@ -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; - -import org.junit.jupiter.api.Test; - -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Client; -import seedu.address.model.person.Lead; -import seedu.address.testutil.TypicalPersons; - -public class ConvertLeadToClientCommandTest { - private Model model = new ModelManager(TypicalPersons.getTypicalAddressBook(), new UserPrefs()); - private Model expectedModel = new ModelManager(TypicalPersons.getTypicalAddressBook(), new UserPrefs()); - - @Test - public void execute_validLeadIndex_success() { - Lead leadToConvert = TypicalPersons.getTypicalLeads().get(0); // assuming you have a list of leads - ConvertLeadToClientCommand command = new ConvertLeadToClientCommand(leadToConvert.getIndex()); - - String expectedMessage = String.format(ConvertLeadToClientCommand.MESSAGE_CONVERT_SUCCESS, leadToConvert); - expectedModel.deletePerson(leadToConvert); - expectedModel.addPerson(new Client(leadToConvert)); // Assuming a constructor for Client is available - - assertCommandSuccess(command, model, expectedMessage, expectedModel); - } - - @Test - public void execute_invalidLeadIndex_failure() { - int outOfBoundIndex = model.getFilteredPersonList().size() + 1; - ConvertLeadToClientCommand command = new ConvertLeadToClientCommand(Index.fromOneBased(outOfBoundIndex)); - - assertCommandFailure(command, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - // Add more test cases for edge cases, if necessary -} -