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:
+
+
### \[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
-}
-