From cf664365735e6e97cf4b45ffce111bfaf72c182f Mon Sep 17 00:00:00 2001 From: leyew <102467346+itsme-zeix@users.noreply.github.com> Date: Mon, 21 Oct 2024 01:13:27 +0800 Subject: [PATCH 01/45] Move saving data section to the last feature --- docs/UserGuide.md | 48 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3cdf1f48280..35c0ff18b89 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -13,13 +13,13 @@ title: User Guide 1. [Quick Start](#quick-start) 2. [Using AgentAssist](#using-agentassist) 3. [Features Overview](#features-overview) - - [Save Current Data](#save-current-data) - [Add New Customer](#add-new-customer) - [Remove Old Customer](#remove-old-customer) - [Edit Existing Customer](#edit-existing-customer) - [Find a Customer by Details](#find-a-customer-by-details) - [Help](#help) - [Exit](#exit) + - [Save Current Data](#saving-data) 4. [FAQ](#faq) 5. [Known Issues](#known-issues) 6. [Command Summary](#command-summary) @@ -105,24 +105,7 @@ Refer to the [Features](#features-overview) section for more detailed instructio ## Features Overview {#features-overview} -### Feature 1: Ability to Save Current Data {#save-current-data} - -**Purpose:** -This feature ensures that any details you add to the app are saved automatically. When you close and reopen the app, all your data will still be there. - -**How it Works:** -- **Command Format and Example:** Not applicable, as this process is automatic. - -#### Parameters -- **Flags and Parameters:** There are no parameters needed for this feature. - -#### What to Expect -- **If Successful:** You can access all the data you've entered previously. -- **If There is an Error:** There's a chance that the data might not be saved due to an error, and you could lose information. - ---- - -### Feature 2: Add New Customer {#add-new-customer} +### Feature 1: Add New Customer {#add-new-customer} **Purpose:** This feature allows you to enter and save detailed records for new customers. Each customer's record includes their name, contact number, email, occupation, and income. You can also enter the optional fields for credit card tier and remark here. Otherwise, new users are assigned a default value of "N.A". @@ -167,7 +150,7 @@ If a customer with the same name, email, job, and income is already saved, you'l --- -### Feature 3: Remove Old Customer {#remove-old-customer} +### Feature 2: Remove Old Customer {#remove-old-customer} **Purpose:** This feature allows you to remove records of customers who are no longer using your credit card services. @@ -201,7 +184,7 @@ Since customer INDEX are unique identifiers: --- -### Feature 4: Edit the existing customer {#edit-existing-customer} +### Feature 3: Edit the existing customer {#edit-existing-customer} **Purpose:** This feature allows users to update the details of an existing customer in the database. All customer information can be modified, including contact details, address, job information, and other relevant data. Additionally, users can either append to or replace existing remarks and adjust the customer's tier status. @@ -259,7 +242,7 @@ This feature allows users to update the details of an existing customer in the d --- -### Feature 5: Find a Customer by Details {#find-a-customer-by-details} +### Feature 4: Find a Customer by Details {#find-a-customer-by-details} **Purpose:** This feature allows users to search for customers by specific details such as name, address, email, phone number, job title, or remarks. @@ -344,7 +327,7 @@ Searches are **case-insensitive** and use [**substring-matching**](#substring-ma --- -### Feature 6: Help {#help} +### Feature 5: Help {#help} **Purpose:** This feature provides users with quick access to the command summary and the user guide for the application, helping them understand how to use various features effectively. @@ -372,7 +355,7 @@ This feature provides users with quick access to the command summary and the use - N/A as this command does not involve processing or displaying data that could involve duplicates. --- -### Feature 7: Exit {#exit} +### Feature 6: Exit {#exit} **Purpose:** Allows users to exit the application through a simple command, eliminating the need to use the window's close button or external controls. @@ -399,6 +382,23 @@ Allows users to exit the application through a simple command, eliminating the n **Handling Duplicates:** - N/A as this command is unique and does not process data that could involve duplicates. +--- + +### Feature 7: Automatically Save Data {#saving-data} + +**Purpose:** +This feature ensures that any details you add to the app are saved automatically. When you close and reopen the app, all your data will still be there. + +**How it Works:** +- **Command Format and Example:** Not applicable, as this process is automatic. + +#### Parameters +- **Flags and Parameters:** There are no parameters needed for this feature. + +#### What to Expect +- **If Successful:** You can access all the data you've entered previously. +- **If There is an Error:** There's a chance that the data might not be saved due to an error, and you could lose information. + -------------------------------------------------------------------------------------------------------------------- ## FAQ {#faq} From d03c9157c658ae05ae631036d630828f9a7c2652 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Mon, 21 Oct 2024 14:23:48 +0800 Subject: [PATCH 02/45] Update fork --- .../predicates/IncomeComparisonPredicate.java | 84 +++++++++++++++++ .../model/util/IncomeComparisonOperator.java | 61 +++++++++++++ .../address/storage/JsonAdaptedTier.java | 52 +++++++++++ .../IncomeComparisonPredicateTest.java | 88 ++++++++++++++++++ .../util/IncomeComparisonOperatorTest.java | 89 +++++++++++++++++++ 5 files changed, 374 insertions(+) create mode 100644 src/main/java/seedu/address/model/person/predicates/IncomeComparisonPredicate.java create mode 100644 src/main/java/seedu/address/model/util/IncomeComparisonOperator.java create mode 100644 src/main/java/seedu/address/storage/JsonAdaptedTier.java create mode 100644 src/test/java/seedu/address/model/person/predicates/IncomeComparisonPredicateTest.java create mode 100644 src/test/java/seedu/address/model/util/IncomeComparisonOperatorTest.java diff --git a/src/main/java/seedu/address/model/person/predicates/IncomeComparisonPredicate.java b/src/main/java/seedu/address/model/person/predicates/IncomeComparisonPredicate.java new file mode 100644 index 00000000000..37f2485d5be --- /dev/null +++ b/src/main/java/seedu/address/model/person/predicates/IncomeComparisonPredicate.java @@ -0,0 +1,84 @@ +package seedu.address.model.person.predicates; + +import static java.util.Objects.requireNonNull; + +import java.util.function.Predicate; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.model.person.Person; +import seedu.address.model.util.IncomeComparisonOperator; + +/** + * Predicate that compares a {@code Person}'s income against a threshold using a specified comparison operator. + */ +public class IncomeComparisonPredicate implements Predicate { + private final int incomeThreshold; + private final IncomeComparisonOperator incomeComparisonOperator; + + /** + * Constructs an {@code IncomeComparisonPredicate}. + * + * @param incomeComparisonOperator The operator used to compare the person's income with the threshold. + * @param incomeThreshold The threshold income to compare against. + */ + public IncomeComparisonPredicate(IncomeComparisonOperator incomeComparisonOperator, int incomeThreshold) { + requireNonNull(incomeComparisonOperator); + checkPositiveIncomeThreshold(incomeThreshold); + this.incomeThreshold = incomeThreshold; + this.incomeComparisonOperator = incomeComparisonOperator; + } + + @Override + public boolean test(Person person) { + int personIncome = person.getIncome().value; + + switch (incomeComparisonOperator.comparisonOperator) { + case "=": + return personIncome == incomeThreshold; + case ">": + return personIncome > incomeThreshold; + case "<": + return personIncome < incomeThreshold; + default: + return false; + } + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof IncomeComparisonPredicate)) { + return false; + } + + IncomeComparisonPredicate otherIncomeComparisonPredicate = + (IncomeComparisonPredicate) other; + return incomeThreshold == otherIncomeComparisonPredicate.incomeThreshold + && incomeComparisonOperator.equals(otherIncomeComparisonPredicate.incomeComparisonOperator); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("incomeThreshold", incomeThreshold) + .add("incomeComparisonOperator", incomeComparisonOperator) + .toString(); + } + + /** + * Ensures that the income threshold is positive. + * + * @param incomeThreshold The threshold to check. + * @throws IllegalArgumentException if {@code incomeThreshold} is not greater than 1. + */ + private void checkPositiveIncomeThreshold(int incomeThreshold) { + if (incomeThreshold < 0) { + throw new IllegalArgumentException("Income threshold cannot be less than 0."); + } + } +} + diff --git a/src/main/java/seedu/address/model/util/IncomeComparisonOperator.java b/src/main/java/seedu/address/model/util/IncomeComparisonOperator.java new file mode 100644 index 00000000000..ed865bee5e8 --- /dev/null +++ b/src/main/java/seedu/address/model/util/IncomeComparisonOperator.java @@ -0,0 +1,61 @@ +package seedu.address.model.util; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import java.util.Objects; + +/** + * Represents a comparison operator used in income comparisons. + * The operator can only be one of '=', '>', or '<'. + * Guarantees: comparisonOperator is validated upon creation. + */ +public class IncomeComparisonOperator { + public static final String MESSAGE_CONSTRAINTS = "Income comparison operators can only be '=', '>' or '<'"; + + public final String comparisonOperator; + + /** + * Constructs a {@code IncomeComparisonOperator}. + * + * @param comparisonOperator A comparisonOperator + */ + public IncomeComparisonOperator(String comparisonOperator) { + requireNonNull(comparisonOperator); + checkArgument(isValidComparisonOperator(comparisonOperator), MESSAGE_CONSTRAINTS); + this.comparisonOperator = comparisonOperator; + } + + /** + * Returns true if a given string is a valid comparison operator. + */ + public static boolean isValidComparisonOperator(String test) { + if (test.trim().isEmpty()) { + return false; + } + + return (test.equals("=") || test.equals(">") || test.equals("<")); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other instanceof IncomeComparisonOperator) { + IncomeComparisonOperator otherIncomeComparisonOperator = (IncomeComparisonOperator) other; + return comparisonOperator.equals(otherIncomeComparisonOperator.comparisonOperator); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(comparisonOperator); + } + + @Override + public String toString() { + return comparisonOperator; + } +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTier.java b/src/main/java/seedu/address/storage/JsonAdaptedTier.java new file mode 100644 index 00000000000..a8b33047621 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedTier.java @@ -0,0 +1,52 @@ +package seedu.address.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.tier.Tier; + +/** + * Jackson-friendly version of {@link Tier}. + */ +class JsonAdaptedTier { + + private final String tierName; + + /** + * Constructs a {@code JsonAdaptedTag} with the given {@code tierName}. + */ + @JsonCreator + public JsonAdaptedTier(String tierName) { + this.tierName = tierName; + } + + /** + * Converts a given {@code Tier} into this class for Jackson use. + */ + public JsonAdaptedTier(Tier source) { + tierName = source.toParsableString(); + } + + @JsonValue + public String getTierName() { + return tierName; + } + + @Override + public String toString() { + return tierName; + } + + /** + * Converts this Jackson-friendly adapted tier object into the model's {@code Tier} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted tier. + */ + public Tier toModelType() throws IllegalValueException { + if (!Tier.isValidTierName(tierName)) { + throw new IllegalValueException(Tier.MESSAGE_CONSTRAINTS); + } + return new Tier(tierName); + } +} diff --git a/src/test/java/seedu/address/model/person/predicates/IncomeComparisonPredicateTest.java b/src/test/java/seedu/address/model/person/predicates/IncomeComparisonPredicateTest.java new file mode 100644 index 00000000000..a4028c79f26 --- /dev/null +++ b/src/test/java/seedu/address/model/person/predicates/IncomeComparisonPredicateTest.java @@ -0,0 +1,88 @@ +package seedu.address.model.person.predicates; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.util.IncomeComparisonOperator; +import seedu.address.testutil.PersonBuilder; + +public class IncomeComparisonPredicateTest { + + @Test + public void equals() { + IncomeComparisonOperator operatorEqual = new IncomeComparisonOperator("="); + IncomeComparisonOperator operatorGreater = new IncomeComparisonOperator(">"); + + IncomeComparisonPredicate firstPredicate = new IncomeComparisonPredicate(operatorEqual, 5000); + IncomeComparisonPredicate secondPredicate = new IncomeComparisonPredicate(operatorGreater, 10000); + + // same object -> returns true + assertTrue(firstPredicate.equals(firstPredicate)); + + // same values -> returns true + IncomeComparisonPredicate firstPredicateCopy = new IncomeComparisonPredicate(operatorEqual, 5000); + assertTrue(firstPredicate.equals(firstPredicateCopy)); + + // different types -> returns false + assertFalse(firstPredicate.equals(1)); + + // null -> returns false + assertFalse(firstPredicate.equals(null)); + + // different predicate -> returns false + assertFalse(firstPredicate.equals(secondPredicate)); + } + + @Test + public void test_incomeEqualComparison_returnsTrue() { + IncomeComparisonPredicate predicate = new IncomeComparisonPredicate(new IncomeComparisonOperator("="), 5000); + assertTrue(predicate.test(new PersonBuilder().withIncome(5000).build())); + } + + @Test + public void test_incomeEqualComparison_returnsFalse() { + IncomeComparisonPredicate predicate = new IncomeComparisonPredicate(new IncomeComparisonOperator("="), 5000); + assertFalse(predicate.test(new PersonBuilder().withIncome(6000).build())); + assertFalse(predicate.test(new PersonBuilder().withIncome(4000).build())); + } + + @Test + public void test_incomeGreaterThanComparison_returnsTrue() { + IncomeComparisonPredicate predicate = new IncomeComparisonPredicate(new IncomeComparisonOperator(">"), 5000); + assertTrue(predicate.test(new PersonBuilder().withIncome(6000).build())); + } + + @Test + public void test_incomeGreaterThanComparison_returnsFalse() { + IncomeComparisonPredicate predicate = new IncomeComparisonPredicate(new IncomeComparisonOperator(">"), 5000); + assertFalse(predicate.test(new PersonBuilder().withIncome(4000).build())); + assertFalse(predicate.test(new PersonBuilder().withIncome(5000).build())); + } + + @Test + public void test_incomeLessThanComparison_returnsTrue() { + IncomeComparisonPredicate predicate = new IncomeComparisonPredicate(new IncomeComparisonOperator("<"), 5000); + assertTrue(predicate.test(new PersonBuilder().withIncome(4000).build())); + } + + @Test + public void test_incomeLessThanComparison_returnsFalse() { + // Income is not less than the threshold + IncomeComparisonPredicate predicate = new IncomeComparisonPredicate(new IncomeComparisonOperator("<"), 5000); + assertFalse(predicate.test(new PersonBuilder().withIncome(6000).build())); + assertFalse(predicate.test(new PersonBuilder().withIncome(5000).build())); + } + + @Test + public void toStringMethod() { + IncomeComparisonOperator operator = new IncomeComparisonOperator("="); + IncomeComparisonPredicate predicate = new IncomeComparisonPredicate(operator, 5000); + + String expected = IncomeComparisonPredicate.class.getCanonicalName() + + "{incomeThreshold=5000, incomeComparisonOperator==}"; + assertEquals(expected, predicate.toString()); + } +} diff --git a/src/test/java/seedu/address/model/util/IncomeComparisonOperatorTest.java b/src/test/java/seedu/address/model/util/IncomeComparisonOperatorTest.java new file mode 100644 index 00000000000..243920102a3 --- /dev/null +++ b/src/test/java/seedu/address/model/util/IncomeComparisonOperatorTest.java @@ -0,0 +1,89 @@ +package seedu.address.model.util; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class IncomeComparisonOperatorTest { + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new IncomeComparisonOperator(null)); + } + + @Test + public void constructor_invalidOperator_throwsIllegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> new IncomeComparisonOperator("!")); + assertThrows(IllegalArgumentException.class, () -> new IncomeComparisonOperator("==")); + assertThrows(IllegalArgumentException.class, () -> new IncomeComparisonOperator("")); + } + + @Test + public void constructor_validOperator_success() { + assertDoesNotThrow(() -> new IncomeComparisonOperator("=")); + assertDoesNotThrow(() -> new IncomeComparisonOperator(">")); + assertDoesNotThrow(() -> new IncomeComparisonOperator("<")); + } + + @Test + public void isValidComparisonOperator() { + // Test valid operators + assertTrue(IncomeComparisonOperator.isValidComparisonOperator("=")); + assertTrue(IncomeComparisonOperator.isValidComparisonOperator(">")); + assertTrue(IncomeComparisonOperator.isValidComparisonOperator("<")); + + // Test invalid operators + assertFalse(IncomeComparisonOperator.isValidComparisonOperator("!")); + assertFalse(IncomeComparisonOperator.isValidComparisonOperator("==")); + assertFalse(IncomeComparisonOperator.isValidComparisonOperator(" ")); + assertFalse(IncomeComparisonOperator.isValidComparisonOperator("")); + assertFalse(IncomeComparisonOperator.isValidComparisonOperator(">=")); + } + + @Test + public void equals_sameObject_returnsTrue() { + IncomeComparisonOperator operator = new IncomeComparisonOperator("="); + assertEquals(operator, operator); + } + + @Test + public void equals_differentObjectsSameValue_returnsTrue() { + IncomeComparisonOperator operator1 = new IncomeComparisonOperator(">"); + IncomeComparisonOperator operator2 = new IncomeComparisonOperator(">"); + assertEquals(operator1, operator2); + } + + @Test + public void equals_differentObjectsDifferentValue_returnsFalse() { + IncomeComparisonOperator operator1 = new IncomeComparisonOperator(">"); + IncomeComparisonOperator operator2 = new IncomeComparisonOperator("<"); + assertNotEquals(operator1, operator2); + } + + @Test + public void hashCode_sameValue_returnsSameHashCode() { + // Test that objects with the same value return the same hash code + IncomeComparisonOperator operator1 = new IncomeComparisonOperator("<"); + IncomeComparisonOperator operator2 = new IncomeComparisonOperator("<"); + assertEquals(operator1.hashCode(), operator2.hashCode()); + } + + @Test + public void hashCode_differentValues_returnsDifferentHashCodes() { + // Test that objects with different values return different hash codes + IncomeComparisonOperator operator1 = new IncomeComparisonOperator("="); + IncomeComparisonOperator operator2 = new IncomeComparisonOperator(">"); + assertNotEquals(operator1.hashCode(), operator2.hashCode()); + } + + @Test + public void toString_returnsCorrectString() { + // Test that the toString method returns the correct string representation + IncomeComparisonOperator operator = new IncomeComparisonOperator("="); + assertEquals("=", operator.toString()); + } +} From e38f1f50648431fc122ffe5bb942ad9681aad875 Mon Sep 17 00:00:00 2001 From: FionaQY Date: Mon, 21 Oct 2024 18:10:37 +0800 Subject: [PATCH 03/45] Implement confirmation check --- .../seedu/address/logic/LogicManager.java | 36 +++++++++++++++++-- .../address/logic/commands/ClearCommand.java | 12 +++++++ .../seedu/address/logic/commands/Command.java | 18 ++++++++++ .../address/logic/commands/CommandResult.java | 12 +++++-- .../address/logic/commands/DeleteCommand.java | 13 +++++++ .../address/logic/commands/ExitCommand.java | 2 +- .../address/logic/commands/HelpCommand.java | 2 +- .../logic/parser/AddressBookParser.java | 11 ++++++ .../seedu/address/logic/LogicManagerTest.java | 31 ++++++++++++++-- .../logic/commands/CommandResultTest.java | 10 +++--- .../logic/commands/ExitCommandTest.java | 2 +- .../logic/commands/HelpCommandTest.java | 2 +- 12 files changed, 136 insertions(+), 15 deletions(-) diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 5aa3b91c7d0..4a7d34d07b6 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -27,12 +27,18 @@ public class LogicManager implements Logic { public static final String FILE_OPS_PERMISSION_ERROR_FORMAT = "Could not save data to file %s due to insufficient permissions to write to the file or the folder."; + public static final String MESSAGE_COMMAND_CANCELLED = "Command has been cancelled."; + private final Logger logger = LogsCenter.getLogger(LogicManager.class); private final Model model; private final Storage storage; private final AddressBookParser addressBookParser; + // Boolean to determine if user has confirmed they want to delete command + private boolean awaitingConfirmation = false; + private Command delayedCommand = null; + /** * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. */ @@ -46,9 +52,16 @@ public LogicManager(Model model, Storage storage) { public CommandResult execute(String commandText) throws CommandException, ParseException { logger.info("----------------[USER COMMAND][" + commandText + "]"); - CommandResult commandResult; + if (this.awaitingConfirmation) { + return handleConfirmation(commandText); + } + Command command = addressBookParser.parseCommand(commandText); - commandResult = command.execute(model); + CommandResult commandResult = command.execute(model, this.awaitingConfirmation); + if (commandResult.isShowConfirmation() && !this.awaitingConfirmation) { + this.awaitingConfirmation = true; + this.delayedCommand = command; + } try { storage.saveAddressBook(model.getAddressBook()); @@ -61,6 +74,25 @@ public CommandResult execute(String commandText) throws CommandException, ParseE return commandResult; } + /** + * Processes user input for command confirmation. + * Executes the delayed command if confirmed, or cancels it and returns a cancellation message. + * + * @param commandText The user's confirmation input. + * @return The result of the command if confirmed, or a cancellation message. + * @throws CommandException If an error occurs during command execution. + */ + private CommandResult handleConfirmation(String commandText) throws CommandException { + CommandResult commandResult; + if (addressBookParser.parseConfirmation(commandText)) { + commandResult = this.delayedCommand.execute(model, this.awaitingConfirmation); + } else { + commandResult = new CommandResult(MESSAGE_COMMAND_CANCELLED); + } + this.awaitingConfirmation = false; + return commandResult; + } + @Override public ReadOnlyAddressBook getAddressBook() { return model.getAddressBook(); diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java index 9c86b1fa6e4..4aa53fda290 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java @@ -12,7 +12,10 @@ public class ClearCommand extends Command { public static final String COMMAND_WORD = "clear"; public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; + public static final String MESSAGE_CLEAR_CONFIRMATION = "This will permanently clear all contacts. " + + "Are you sure you want to execute this command? (y/n)"; + private static final boolean requiresConfirmation = true; @Override public CommandResult execute(Model model) { @@ -20,4 +23,13 @@ public CommandResult execute(Model model) { model.setAddressBook(new AddressBook()); return new CommandResult(MESSAGE_SUCCESS); } + + @Override + public CommandResult execute(Model model, Boolean confirmationReceived) { + if (confirmationReceived.equals(requiresConfirmation)) { + return this.execute(model); + } + return new CommandResult(MESSAGE_CLEAR_CONFIRMATION, false, false, true); + } + } diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/address/logic/commands/Command.java index 64f18992160..94b5f420af7 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/address/logic/commands/Command.java @@ -8,6 +8,24 @@ */ public abstract class Command { + private static final boolean requiresConfirmation = false; + + /** + * Executes the command and returns the result message. + * + * @param model {@code Model} which the command should operate on and m + * @param confirmationReceived A {@code Boolean} indicating whether user confirmation has been provided, + * if required for executing the command. + * @return feedback message of the operation result for display + * @throws CommandException If an error occurs during command execution. + */ + public CommandResult execute(Model model, Boolean confirmationReceived) throws CommandException { + if (confirmationReceived.equals(requiresConfirmation)) { + return this.execute(model); + } + return null; + } + /** * Executes the command and returns the result message. * diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java index 249b6072d0d..a0543f315e5 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/address/logic/commands/CommandResult.java @@ -19,13 +19,17 @@ public class CommandResult { /** The application should exit. */ private final boolean exit; + /** The application should show a confirmation button */ + private final boolean showConfirmation; + /** * Constructs a {@code CommandResult} with the specified fields. */ - public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { + public CommandResult(String feedbackToUser, boolean showHelp, boolean exit, boolean showConfirmation) { this.feedbackToUser = requireNonNull(feedbackToUser); this.showHelp = showHelp; this.exit = exit; + this.showConfirmation = showConfirmation; } /** @@ -33,7 +37,7 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { * and other fields set to their default value. */ public CommandResult(String feedbackToUser) { - this(feedbackToUser, false, false); + this(feedbackToUser, false, false, false); } public String getFeedbackToUser() { @@ -48,6 +52,10 @@ public boolean isExit() { return exit; } + public boolean isShowConfirmation() { + return showConfirmation; + } + @Override public boolean equals(Object other) { if (other == this) { diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java index 1135ac19b74..bfef87851be 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java @@ -24,6 +24,11 @@ public class DeleteCommand extends Command { + "Example: " + COMMAND_WORD + " 1"; public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; + public static final String MESSAGE_DELETE_CONFIRMATION = "This will permanently delete this contact. " + + "Are you sure you want to execute this command? (y/n)"; + + private static final boolean requiresConfirmation = true; + private final Index targetIndex; @@ -45,6 +50,14 @@ public CommandResult execute(Model model) throws CommandException { return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete))); } + @Override + public CommandResult execute(Model model, Boolean confirmationReceived) throws CommandException { + if (confirmationReceived.equals(requiresConfirmation)) { + return this.execute(model); + } + return new CommandResult(MESSAGE_DELETE_CONFIRMATION, false, false, true); + } + @Override public boolean equals(Object other) { if (other == this) { diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java index 3dd85a8ba90..acac9a21374 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java @@ -13,7 +13,7 @@ public class ExitCommand extends Command { @Override public CommandResult execute(Model model) { - return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false); } } diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java index bf824f91bd0..07d26e2a23c 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java @@ -16,6 +16,6 @@ public class HelpCommand extends Command { @Override public CommandResult execute(Model model) { - return new CommandResult(SHOWING_HELP_MESSAGE, true, false); + return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false); } } diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index bc2f04c5ab5..28b0e9a5bfa 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -84,4 +84,15 @@ public Command parseCommand(String userInput) throws ParseException { } } + /** + * Parses user input into command for execution. + * + * @param userInput full user input string + * @return boolean depending on whether the input is yes/y + */ + public boolean parseConfirmation(String userInput) { + userInput = userInput.replace("\n", "").toLowerCase().trim(); + return userInput.equals("y") || userInput.equals("yes"); + } + } diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java index 617445f4582..65c0c62ef4a 100644 --- a/src/test/java/seedu/address/logic/LogicManagerTest.java +++ b/src/test/java/seedu/address/logic/LogicManagerTest.java @@ -9,6 +9,7 @@ import static seedu.address.logic.commands.CommandTestUtil.JOB_DESC_AMY; import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; +import static seedu.address.logic.commands.DeleteCommand.MESSAGE_DELETE_CONFIRMATION; import static seedu.address.testutil.Assert.assertThrows; import static seedu.address.testutil.TypicalPersons.AMY; @@ -60,9 +61,16 @@ public void execute_invalidCommandFormat_throwsParseException() { } @Test - public void execute_commandExecutionError_throwsCommandException() { + public void execute_deleteCommandRequiresConfirmation_displaysDeleteConfirmationMessage() + throws CommandException, ParseException { String deleteCommand = "delete 9"; - assertCommandException(deleteCommand, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + assertCommandSuccess(deleteCommand, MESSAGE_DELETE_CONFIRMATION, model); + } + + @Test + public void execute_commandExecutionError_throwsCommandException() throws CommandException, ParseException { + String[] deleteCommand = {"delete 9", "y"}; + assertDeleteCommandFailure(CommandException.class, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX, deleteCommand); } @Test @@ -88,6 +96,25 @@ public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredPersonList().remove(0)); } + /** + * Executes the command (which requires a confirmation) and confirms that + * - the {@code expectedException} is thrown
+ * - the resulting error message is equal to {@code expectedMessage}
+ * @see #assertCommandSuccess(String, String, Model) + */ + private void assertDeleteCommandFailure(Class expectedException, String expectedMessage, + String... inputCommand) throws CommandException, ParseException { + for (int i = 0; i < inputCommand.length; i++) { + if (i == inputCommand.length - 1) { + assertThrows(expectedException, expectedMessage, () -> + logic.execute(inputCommand[inputCommand.length - 1])); + } else { + logic.execute(inputCommand[i]); + } + } + } + + /** * Executes the command and confirms that * - no exceptions are thrown
diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/seedu/address/logic/commands/CommandResultTest.java index 7b8c7cd4546..46e476a09d4 100644 --- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java +++ b/src/test/java/seedu/address/logic/commands/CommandResultTest.java @@ -14,7 +14,7 @@ public void equals() { // same values -> returns true assertTrue(commandResult.equals(new CommandResult("feedback"))); - assertTrue(commandResult.equals(new CommandResult("feedback", false, false))); + assertTrue(commandResult.equals(new CommandResult("feedback", false, false, false))); // same object -> returns true assertTrue(commandResult.equals(commandResult)); @@ -29,10 +29,10 @@ public void equals() { assertFalse(commandResult.equals(new CommandResult("different"))); // different showHelp value -> returns false - assertFalse(commandResult.equals(new CommandResult("feedback", true, false))); + assertFalse(commandResult.equals(new CommandResult("feedback", true, false, false))); // different exit value -> returns false - assertFalse(commandResult.equals(new CommandResult("feedback", false, true))); + assertFalse(commandResult.equals(new CommandResult("feedback", false, true, false))); } @Test @@ -46,10 +46,10 @@ public void hashcode() { assertNotEquals(commandResult.hashCode(), new CommandResult("different").hashCode()); // different showHelp value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false).hashCode()); + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false, false).hashCode()); // different exit value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true).hashCode()); + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true, false).hashCode()); } @Test diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java index 9533c473875..a573f42f11b 100644 --- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java @@ -14,7 +14,7 @@ public class ExitCommandTest { @Test public void execute_exit_success() { - CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false); assertCommandSuccess(new ExitCommand(), model, expectedCommandResult, expectedModel); } } diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java index 4904fc4352e..3b35c387c4c 100644 --- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java @@ -14,7 +14,7 @@ public class HelpCommandTest { @Test public void execute_help_success() { - CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false); + CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false, false); assertCommandSuccess(new HelpCommand(), model, expectedCommandResult, expectedModel); } } From 1876f0d031c94a2a5f9a497bb2020bf57f66c202 Mon Sep 17 00:00:00 2001 From: FionaQY Date: Mon, 21 Oct 2024 18:24:31 +0800 Subject: [PATCH 04/45] Refactor command logic --- src/main/java/seedu/address/logic/commands/AddCommand.java | 2 +- src/main/java/seedu/address/logic/commands/ClearCommand.java | 2 +- src/main/java/seedu/address/logic/commands/Command.java | 3 +-- src/main/java/seedu/address/logic/commands/ExitCommand.java | 2 +- src/main/java/seedu/address/logic/commands/FilterCommand.java | 2 +- src/main/java/seedu/address/logic/commands/HelpCommand.java | 2 +- src/main/java/seedu/address/logic/commands/ListCommand.java | 2 +- 7 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java index 668cf152221..d3d0ea6f208 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddCommand.java @@ -57,7 +57,7 @@ public AddCommand(Person person) { } @Override - public CommandResult execute(Model model) throws CommandException { + protected CommandResult execute(Model model) throws CommandException { requireNonNull(model); if (model.hasPerson(toAdd)) { diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java index 4aa53fda290..42ad7f220e2 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java @@ -18,7 +18,7 @@ public class ClearCommand extends Command { private static final boolean requiresConfirmation = true; @Override - public CommandResult execute(Model model) { + protected CommandResult execute(Model model) { requireNonNull(model); model.setAddressBook(new AddressBook()); return new CommandResult(MESSAGE_SUCCESS); diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/address/logic/commands/Command.java index 94b5f420af7..278fca620a2 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/address/logic/commands/Command.java @@ -33,6 +33,5 @@ public CommandResult execute(Model model, Boolean confirmationReceived) throws C * @return feedback message of the operation result for display * @throws CommandException If an error occurs during command execution. */ - public abstract CommandResult execute(Model model) throws CommandException; - + protected abstract CommandResult execute(Model model) throws CommandException; } diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java index acac9a21374..d8f83f2cf55 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java @@ -12,7 +12,7 @@ public class ExitCommand extends Command { public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; @Override - public CommandResult execute(Model model) { + protected CommandResult execute(Model model) { return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false); } diff --git a/src/main/java/seedu/address/logic/commands/FilterCommand.java b/src/main/java/seedu/address/logic/commands/FilterCommand.java index 7c562afac2d..87c62d20b93 100644 --- a/src/main/java/seedu/address/logic/commands/FilterCommand.java +++ b/src/main/java/seedu/address/logic/commands/FilterCommand.java @@ -31,7 +31,7 @@ public FilterCommand(Predicate predicate) { } @Override - public CommandResult execute(Model model) { + protected CommandResult execute(Model model) { requireNonNull(model); model.updateFilteredPersonList(predicate); return new CommandResult( diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java index 07d26e2a23c..a613711e4ac 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java @@ -15,7 +15,7 @@ public class HelpCommand extends Command { public static final String SHOWING_HELP_MESSAGE = "Opened help window."; @Override - public CommandResult execute(Model model) { + protected CommandResult execute(Model model) { return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false); } } diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java index 84be6ad2596..1320c632340 100644 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ b/src/main/java/seedu/address/logic/commands/ListCommand.java @@ -16,7 +16,7 @@ public class ListCommand extends Command { @Override - public CommandResult execute(Model model) { + protected CommandResult execute(Model model) { requireNonNull(model); model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); return new CommandResult(MESSAGE_SUCCESS); From c9bf45f45e8112af8247e72f59473228d957580d Mon Sep 17 00:00:00 2001 From: FionaQY Date: Mon, 21 Oct 2024 18:35:10 +0800 Subject: [PATCH 05/45] Fix saving bug --- .../java/seedu/address/logic/LogicManager.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 4a7d34d07b6..b4ab309d425 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -52,15 +52,17 @@ public LogicManager(Model model, Storage storage) { public CommandResult execute(String commandText) throws CommandException, ParseException { logger.info("----------------[USER COMMAND][" + commandText + "]"); + CommandResult commandResult; if (this.awaitingConfirmation) { - return handleConfirmation(commandText); - } + commandResult = handleConfirmation(commandText); + } else { + Command command = addressBookParser.parseCommand(commandText); + commandResult = command.execute(model, this.awaitingConfirmation); - Command command = addressBookParser.parseCommand(commandText); - CommandResult commandResult = command.execute(model, this.awaitingConfirmation); - if (commandResult.isShowConfirmation() && !this.awaitingConfirmation) { - this.awaitingConfirmation = true; - this.delayedCommand = command; + if (commandResult.isShowConfirmation() && !this.awaitingConfirmation) { + this.awaitingConfirmation = true; + this.delayedCommand = command; + } } try { From 3eec0eade07f488fe0ed1cf9a2fbad3bc0bd2bc3 Mon Sep 17 00:00:00 2001 From: FionaQY Date: Mon, 21 Oct 2024 18:43:00 +0800 Subject: [PATCH 06/45] Fix checkstyle violation --- src/main/java/seedu/address/logic/LogicManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index b4ab309d425..4f24f883ead 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -54,7 +54,7 @@ public CommandResult execute(String commandText) throws CommandException, ParseE CommandResult commandResult; if (this.awaitingConfirmation) { - commandResult = handleConfirmation(commandText); + commandResult = handleConfirmation(commandText); } else { Command command = addressBookParser.parseCommand(commandText); commandResult = command.execute(model, this.awaitingConfirmation); From 7d77a326f49e5bd3d0065c7fc9493d95067abd9d Mon Sep 17 00:00:00 2001 From: FionaQY Date: Mon, 21 Oct 2024 21:21:54 +0800 Subject: [PATCH 07/45] Add test for delete command --- .../seedu/address/logic/LogicManager.java | 4 +-- .../java/seedu/address/logic/Messages.java | 3 +++ .../seedu/address/logic/LogicManagerTest.java | 26 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 4f24f883ead..41426a60aae 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -18,6 +18,8 @@ import seedu.address.model.person.Person; import seedu.address.storage.Storage; +import static seedu.address.logic.Messages.MESSAGE_COMMAND_CANCELLED; + /** * The main LogicManager of the app. */ @@ -27,8 +29,6 @@ public class LogicManager implements Logic { public static final String FILE_OPS_PERMISSION_ERROR_FORMAT = "Could not save data to file %s due to insufficient permissions to write to the file or the folder."; - public static final String MESSAGE_COMMAND_CANCELLED = "Command has been cancelled."; - private final Logger logger = LogsCenter.getLogger(LogicManager.class); private final Model model; diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java index 6174a241348..25ea2b5ad5b 100644 --- a/src/main/java/seedu/address/logic/Messages.java +++ b/src/main/java/seedu/address/logic/Messages.java @@ -21,6 +21,9 @@ public class Messages { public static final String MESSAGE_CONCURRENT_RN_RA_FIELDS = "Both remark new and remark append fields are " + "specified, please only use one."; + + public static final String MESSAGE_COMMAND_CANCELLED = "Command has been cancelled."; + /** * Returns an error message indicating the duplicate prefixes. */ diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java index 65c0c62ef4a..d4d6601a7f2 100644 --- a/src/test/java/seedu/address/logic/LogicManagerTest.java +++ b/src/test/java/seedu/address/logic/LogicManagerTest.java @@ -1,6 +1,7 @@ package seedu.address.logic; import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.address.logic.Messages.MESSAGE_COMMAND_CANCELLED; import static seedu.address.logic.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND; import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; @@ -67,6 +68,12 @@ public void execute_deleteCommandRequiresConfirmation_displaysDeleteConfirmation assertCommandSuccess(deleteCommand, MESSAGE_DELETE_CONFIRMATION, model); } + @Test + public void execute_cancelDeleteCommand_success() throws CommandException, ParseException { + String[] deleteCommand = {"delete 9", "no"}; + assertDeleteCommandSuccess(MESSAGE_COMMAND_CANCELLED, model, deleteCommand); + } + @Test public void execute_commandExecutionError_throwsCommandException() throws CommandException, ParseException { String[] deleteCommand = {"delete 9", "y"}; @@ -200,4 +207,23 @@ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) expectedModel.addPerson(expectedPerson); assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel); } + + /** + * Executes the command (which requires a confirmation) and confirms that + * - no exceptions are thrown
+ * - the feedback message is equal to {@code expectedMessage}
+ * - the internal model manager state is the same as that in {@code expectedModel}
+ * @see #assertCommandFailure(String, Class, String, Model) + */ + private void assertDeleteCommandSuccess(String expectedMessage, Model expectedModel, + String... inputCommand) throws CommandException, ParseException { + for (int i = 0; i < inputCommand.length; i++) { + if (i == inputCommand.length - 1) { + CommandResult result = logic.execute(inputCommand[inputCommand.length - 1]); + assertEquals(expectedMessage, result.getFeedbackToUser()); + } else { + logic.execute(inputCommand[i]); + } + } + } } From 97c0dcf9cdd3a3bf05e8341cd50964f09423574e Mon Sep 17 00:00:00 2001 From: FionaQY Date: Mon, 21 Oct 2024 21:26:14 +0800 Subject: [PATCH 08/45] Fix import order --- src/main/java/seedu/address/logic/LogicManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 41426a60aae..9e3d984d520 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -1,5 +1,7 @@ package seedu.address.logic; +import static seedu.address.logic.Messages.MESSAGE_COMMAND_CANCELLED; + import java.io.IOException; import java.nio.file.AccessDeniedException; import java.nio.file.Path; @@ -18,8 +20,6 @@ import seedu.address.model.person.Person; import seedu.address.storage.Storage; -import static seedu.address.logic.Messages.MESSAGE_COMMAND_CANCELLED; - /** * The main LogicManager of the app. */ From 80442917718d7b20fa7c7adcfa7e499bed47b017 Mon Sep 17 00:00:00 2001 From: leyew <102467346+itsme-zeix@users.noreply.github.com> Date: Mon, 21 Oct 2024 22:27:05 +0800 Subject: [PATCH 09/45] Rewrite of User Guide --- docs/UserGuide.md | 880 ++++++++++++++++++++++++++++++---------------- 1 file changed, 576 insertions(+), 304 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 35c0ff18b89..d98794846ca 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -3,436 +3,708 @@ layout: page title: User Guide --- ![Banner](images/AgentAssistBanner.png) -## AgentAssist -**Transform Your Credit Card Sales with AgentAssist** — the definitive desktop tool for bank agents. Merging the swift efficiency of a Command Line Interface (CLI) with the intuitive accessibility of a Graphical User Interface (GUI), AgentAssist propels your credit card sales into the fast lane. Tailored for the agile bank agent, this application lets you manage contact databases, track sales progress, and execute transactions with unprecedented speed. Maximize your productivity, minimize your response time, and amplify your sales performance. With AgentAssist, you're not just keeping up with the competitive world of credit card sales — _you're setting the pace_. +# Welcome to the AgentAssist User Guide! ---- +The **AgentAssist User Guide** is here to help you unlock the full potential of **AgentAssist** and take your credit card sales to the next level. his guide offers clear, step-by-step instructions and practical examples to help you get the most out of the application. + +In this guide, you'll learn how to: +* **Set Up AgentAssist** +* **Navigate and Use Key Features** like contact management, filtering, and more. +* **Optimize Your Workflow** with shortcuts, data export/import, and automatic saving. -## Table of Contents - -1. [Quick Start](#quick-start) -2. [Using AgentAssist](#using-agentassist) -3. [Features Overview](#features-overview) - - [Add New Customer](#add-new-customer) - - [Remove Old Customer](#remove-old-customer) - - [Edit Existing Customer](#edit-existing-customer) - - [Find a Customer by Details](#find-a-customer-by-details) - - [Help](#help) - - [Exit](#exit) - - [Save Current Data](#saving-data) -4. [FAQ](#faq) -5. [Known Issues](#known-issues) -6. [Command Summary](#command-summary) +Let’s begin and get you up to speed with AgentAssist! -------------------------------------------------------------------------------------------------------------------- -## Quick start {#quick-start} +# Table of Contents -### 1. Install Java +1. [Introduction](#1-introduction) +
+ Subsections -Make sure you have **Java 17 or above** installed on your computer. You may skip this step if you already have it installed. + 1.1 [What is AgentAssist?](#11-what-is-agentassist) + 1.2 [Why use AgentAssist?](#12-why-use-agentassist) -Installing Java: +
-* Download Java [here](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html). Follow the instructions on that page to install Java. +2. [Important Prerequisites](#2-important-prerequisites) -### 2. Download the AgentAssist application +3. [Getting Started](#3-getting-started) +
+ Subsections -Go to this [link](https://github.com/AY2425S1-CS2103T-T14-4/tp/releases) and download the latest version of the `.jar` file. + 3.1 [Installation](#31-installation) + 3.2 [Graphical User Interface (GUI) Layout](#32-graphical-user-interface-gui-layout) -### 3. Choose a Folder +
-Find or create a folder on your computer where you want to store the AgentAssist information.
-Move the `.jar` file you just downloaded into this folder. +4. [Understanding Commands in AgentAssist](#4-understanding-commands-in-agentassist) +
+ Subsections -### 4. Open a Command Terminal + 4.1 [Command Structure Overview](#41-command-structure-overview) + 4.2 [Commands](#42-commands) + 4.3 [Flags](#43-flags) + 4.4 [Arguments](#44-arguments) + 4.5 [Using Commands](#45-using-commands) -Now, open a command terminal: +
-* On Windows, press the **Windows Key** and type **`Command Prompt`** in the search bar. Click on the **Command Prompt** application to open it. -* On macOS, press **Cmd + Space**, type **`Terminal`** into the search bar, and press **Enter**. -* On Linux, open your Terminal application. +5. [Commands](#5-commands) +
+ Subsections -### 5. Run the applicaton + 5.1 [How to Read Commands](#51-how-to-read-commands) + 5.2 [Data Modification Commands](#52-data-modification-commands) + 5.3 [Data Filtering Commands](#53-data-filtering-commands) + 5.4 [General Commands](#54-general-commands) + 5.5 [Saving Data](#55-saving-data) + 5.6 [Modifying the Data File](#56-modifying-the-data-file) -Navigate your terminal to the folder where you saved the AgentAssist application: +
+ +6. [FAQ](#6-faq) +7. [Known Issues](#7-known-issues) +8. [Command Summary](#8-command-summary) -* Type **`cd `** (with a space after it), then drag and drop the folder where you placed the `.jar` file into the terminal window. The command should look similar to this: `cd '/Users/name/AgentAssistFolder'`. Press Enter.
-Type the following command: **`java -jar agentassist.jar`** and press **Enter**. +# 1. Introduction +## 1.1 What is AgentAssist? -* A window similar to the below image should appear in a few seconds. You will see a graphical user interface with sample contact information already added.
- ![Ui](images/Ui.png) +AgentAssist is the **definitive desktop tool for credit card sales agents**. Merging the swift efficiency of a Command Line Interface (CLI) with the intuitive accessibility of a Graphical User Interface (GUI), this application lets you manage contact databases, track sales progress, and execute transactions with unprecedented speed. ---- +**Overview of Key Features:** +* **Contact Management**: + * Manage your client details easily. Add, edit, and delete contacts to keep all your client information in one accessible place. +* **Keyboard-only Navigation**: + * Navigate through the application entirely via keyboard shortcuts, improving workflow efficiency. +* **Multi-Level Filtering**: + * Filter your data by multiple criteria to find exactly who you’re looking for. +* **Auto-Save**: + * Automatically saves your work as you go, ensuring data is updated without manual intervention. +* **Effortless Data Import & Export**: + * Import or export client and sales data in compatible formats for backups or use in other systems. + +Maximize your productivity, minimize your response time, and amplify your sales performance. With AgentAssist, you're not just keeping up with the competitive world of credit card sales — _you're setting the pace_. + +## 1.2 Why use AgentAssist? + +-------------------------------------------------------------------------------------------------------------------- + +# 2. Important Prerequisites + +# 3. Getting Started +Welcome to AgentAssist. Here’s how to get up and running quickly and easily. + +## 3.1 Installation +### Step 1: Install Java + +Ensure you have **Java 17** installed on your computer. AgentAssist is optimized for **Java 17**, and using other versions may affect performance or functionality. If you already have Java 17 installed, you can skip this step. + +To install Java 17: +* Visit the Java download page from [Oracle](https://www.oracle.com/java/technologies/downloads/#java17?er=221886). +* Download the appropriate installer for your operating system (Windows, macOS, or Linux). +* Follow the installation instructions on the website to complete the setup. +* Once installed, verify the installation by [opening your terminal (or command prompt)](#how-to-open-terminal) and typing: + ``` + java -version + ``` +* If you see Java 17 in the output, you’re good to go! + +### Step 2: Download the AgentAssist application + +Download the latest version of the `.jar` file from the AgentAssist [repository](https://github.com/AY2425S1-CS2103T-T14-4/tp/releases). + +[comment]: # (TODO: Add image of GitHub Releases page with annotations to show user the file to install.) + +### Step 3: Choose a Folder + +Find or create a folder on your computer where you want to store the AgentAssist application and its data. +Move the .jar file you downloaded into this folder. + +### Step 4: Run the Application + +1. **Open a command terminal** + - On Windows, press `Windows Key + R`, type `cmd`, and press `Enter`. + - On macOS, press `Command + Space`, type `Terminal`, and press `Enter`. + - On Linux, open your **Terminal** application from the system menu. + +2. **Navigate your terminal to the folder where you saved the AgentAssist application:** + - To do this, use the `cd` command followed by the path to your folder.\ + > ℹ️ **Tip:** Follow the guide below to navigate to your folder in the terminal: + > + >
Click here to learn how to navigate to your folder in terminal + > + > - **Windows**: Use the command `cd `. + For example, if **AgentAssist** is stored in the `Downloads` folder: + > ```bash + > cd C:\Users\\Downloads + > ``` + > + > - **macOS/Linux**: Use the command `cd `. + For example, if **AgentAssist** is stored in the `Downloads` folder: + > ```bash + > cd /Users//Downloads + > ``` + > + >
+ +3. Run the application: + - Type the following command: **`java -jar agentassist.jar`** and press **Enter**. + - A window similar to the below image should appear in a few seconds. You will see a graphical user interface with sample contact information already added.

+ ![Ui](images/Ui.png) + + +## 3.2 Graphical User Interface (GUI) Layout + + +To learn more about how to use commands in AgentAssist, proceed to the next section. + +-------------------------------------------------------------------------------------------------------------------- + +# 4. Understanding Commands in AgentAssist {#using-agentassist} + +The true power of **AgentAssist** lies in efficiently using commands. Before diving into specific commands, let’s break down the basic structure of a command. + +## 4.1 Command Structure Overview +Each command in AgentAssist consists of three key components: the **command**, **flag(s)**, and **argument(s)**. + +Let's take a look at the structure in more detail: + +| **Components** | **Description** | **Example** | +|:----------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------| +| **Command** | The action you want AgentAssist to perform. | `add` | +| **Flag(s)** | Modifiers that specify what kind of data is being handled.

Flag(s) are typically 1-2 letters followed by a backslash. | `n/`, `p/`, `r/`, `rn/` | +| **Argument(s)** | The values or inputs the command uses, such as client data or specific details.

The user guide may represent it as a placeholder using ``. | `John Doe`, `john@example.com` | + +Here's an example that uses multiple flags and arguments: +``` +add n/ John Doe e/ john@example.com +``` +* **Command:** `add` instructs AgentAssist to add a new entry. +* **Flags:** `n/` and `e/` specify the information type (name and email). +* **Arguments:** `John Doe` and `john@example.com` are the actual values being input for the respective flags. + + +## 4.2 Commands +A command is the action that AgentAssist will perform, such as adding, deleting, or editing a contact. + +Here is a reference table that briefly summarizes available commands: + +| **Command** | **Description** | +|-------------|--------------------------------------------------------| +| `add` | Adds a new client to the system. | +| `edit` | Modifies details of an existing client. | +| `delete` | Removes a client from the system. | +| `list` | Displays all clients currently stored in the system. | +| `filter` | Filters clients based on specified criteria | +| `clear` | Deletes all clients from the system. | +| `help` | Displays a list of available commands and their usage. | +| `exit` | Exits the AgentAssist application. | -## Using AgentAssist {#using-agentassist} +Refer to the [Commands Section](#commands-section) for more comprehensive details of each command. -AgentAssist's command box is your portal to managing contacts efficiently. Here’s how you can use it to streamline your tasks: +## 4.3 Flags -* To get started, simply type a command into the command box and hit **Enter**. +AgentAssist uses flags as a shorthand for different options in commands. Flags help you specify what kind of information you are providing, allowing you to write shorter and more efficient commands, improving your workflow. -Some initial commands to try: +Here’s a reference table of available flags and the type of data they correspond to: +| **Flag** | **Type of Data** | +|----------|------------------| +| `i/` | `index` | +| `n/` | `name` | +| `p/` | `phone` | +| `e/` | `email` | +| `a/` | `address` | +| `j/` | `job` | +| `i/` | `income` | +| `t/` | `tier` | +| `r/` | `remark` | +| `ra/` | `remark append` | +| `rn/` | `remark new` | -- **Viewing Contacts** - * `list`: This command displays all contacts currently in your database, making it easy to browse through entries. +> 💡 **Pro Tip:** +> +> Flags are typically derived from the first letter of their corresponding data type (e.g., `n/` for `name`), making them easy to remember! -- **Adding a New Contact** - * `add n/Jane Doe p/87654321 e/jane@example.com a/123 Jane Road j/doctor i/120000`: Adds Jane Doe to your database with detailed contact info, occupation, and income. +## 4.4 Arguments -- **Editing a Contact** - * `edit 1 p/12345678`: Updates the phone number of the first contact in your list to `12345678`. - * `edit 4 rn/Updated remarks here`: Replaces the remarks of the fourth contact with "Updated remarks here". +Arguments are the values that follow each flag in a command. **Arguments cannot be empty**, and each must meet specific parsing and format requirements to ensure proper execution of the command. -- **Removing a Contact** - * `delete 3`: Removes the third contact from your list. Ensure you have the correct index to avoid deleting the wrong entry. +Refer to the table below for more details. -- **Searching for a Contact** - * `filter n/Jane`: Finds all contacts named Jane in your database. It’s a powerful tool for quickly locating entries. +| **Flag** | **Expected Argument** | **Description** | **Requirements** | **Case Sensitivity** | +|----------|-----------------------|------------------------------------------------|-------------------------------------------------------------------------------------------------|-----------------------| +| `n/` | `` | The client's full name | Letters, numbers, and spaces only | ❌ | +| `p/` | `` | The client's phone number | Valid Singapore phone number:
• 8-digit number
• Starts with 8 or 9 | ❌ | +| `e/` | `` | The client's email address | Valid email format (`username@domain.com`) | ❌ | +| `a/` | `
` | The client's physical address | No specific restrictions | ❌ | +| `j/` | `` | The client's job title or profession | No specific restrictions | ❌ | +| `i/` | `` | The client's annual income | Positive number or zero
• Cannot include commas and decimal points
• Must be numeric | ❌ | +| `t/` | `` | The client's assigned tier level | Predefined tiers:
• Gold, Silver, Bronze, Reject | ✔️ | +| `r/` | `` | General remarks about the client | No specific restrictions | ❌ | +| `ra/` | `` | Append information to the existing remark | No specific restrictions | ❌ | +| `rn/` | `` | Replaces the existing remark with a new remark | No specific restrictions | ❌ | -- **Getting Help** - * `help`: Opens a help dialog that provides a summary of all available commands and their usage. +> 💡 **Pro Tip:** +> +> Ensure every flag is followed by a valid argument! +> +> Providing a flag without an accompanying argument will result in an error and prevent the command from executing properly. +## 4.5 Using Commands +To get started, simply type a command into the command box and hit **Enter**. -**Pro Tip:** Experiment with combining commands like `filter` followed by `edit` or `delete` to manage your contacts more effectively. For example, use `filter j/doctor` to display all doctors, then `edit 2 a/321 New Address` to update the address for the second listed doctor. +Some initial commands to try: +**Viewing All Clients** +* `list`: This command displays all clients currently in your database, making it easy to browse through entries. + +**Adding a New Client** +* `add n/Jane Doe p/87654321 e/jane@example.com a/123 Jane Road j/doctor i/120000`: Adds Jane Doe to your database with detailed contact information, job title, and income. + +**Editing a Client's Information** +* `edit 1 p/12345678`: Updates the phone number of the first client in your list to `12345678`. +* `edit 4 rn/Updated remarks here`: Replaces the remarks of the fourth client with "Updated remarks here". + +**Removing a Client** +* `delete 3`: Removes the third client from your list. Ensure you have the correct index to avoid deleting the wrong client. + +**Searching for a Client** +* `filter n/Jane`: Finds all client named Jane in your database. It’s a powerful tool for quickly locating clients or filtering for a specific type of client. + +**Getting Help** +* `help`: Opens a help dialog that provides a summary of all available commands and their usage. The GUI will dynamically update to show the results of your commands, making it easy to see the impact of your actions in real time. -Refer to the [Features](#features-overview) section for more detailed instructions on each command. +Refer to the [Commands Section](#commands-section) for more comprehensive details of each command. ---- +> 💡 **Pro Tip:** +> Combine commands like `filter` followed by `edit` or `delete` to manage your contacts more effectively. +> For example, use `filter j/doctor` to display all doctors, then `edit 2 a/321 New Address` to update the address for the second listed doctor. -## Features Overview {#features-overview} +-------------------------------------------------------------------------------------------------------------------- -### Feature 1: Add New Customer {#add-new-customer} +# 5. Commands -**Purpose:** -This feature allows you to enter and save detailed records for new customers. Each customer's record includes their name, contact number, email, occupation, and income. You can also enter the optional fields for credit card tier and remark here. Otherwise, new users are assigned a default value of "N.A". +## 5.1 How to Read Commands -**How to Use It:** - - **Command Format:** - ``` - add n/ p/ e/ a/
j/ i/ [t/ ] [rn/ ] - ``` - - **Examples:** - - Add new customer, basic convention: - ``` - add n/ TAN LESHEW p/ 99007766 e/ mrtan@ntu.sg a/ com3 j/ doctor i/ 99999 - ``` - - Add new customer with tier and remark: - ``` - add n/ TAN LESHEW p/ 99007766 e/ mrtan@ntu.sg a/ com3 j/ doctor i/ 99999 t/ gold rn/ got anger issue - ``` - +When working with commands in **AgentAssist**, it's important to understand **how the command format is structured**. Commands consist of specific components like **flags** and **arguments**, and some parts of the command can be **optional**. -#### Parameters {#add-command-parameters} +If you're unfamiliar with how commands are structured, refer back to the [Command Structure Overview in Section 4.1](#command-structure-overview) for more details on how flags, arguments, and placeholders work together. -| Parameter | Expected Format | Explanation | -|-----------|--------------------------------------------------|-------------------------------------------------------------------------------------------------------------------| -| NAME | Alphanumeric, case insensitive | Accepts all names without case sensitivity. Names will be displayed in block letters for clarity and consistency. | -| PHONE | 8-digit number, starts with 8 or 9 | Ensures the contact number is valid in Singapore. | -| EMAIL | Must include "@" and domain, case insensitive | Verifies that the email address is in a standard format. | -| ADDRESS | Any text, case insensitive | Accepts all addresses without case sensitivity. Addresses can have numbers and symbol alike /. | -| JOB | Any text, case insensitive | Accepts all job titles without case sensitivity. | -| INCOME | Non-negative integers | Only positive numbers or zero are valid for income fields. | -| TIER | [optional] String (gold, silver, bronze, reject) | Defines the specific credit card tier to be assigned or updated. | -| REMARK | [optional] Any string | Notes are case-insensitive and can include any textual information. | +### Command Syntax -#### What to Expect -- **If Successful:** - - Message: "New person added: ``; Phone: ``; Email: ``; Address: `
`; Job: ``; Income: ``; Tier: ``; Remark: ``". It's noted that if "Tier" and "Remark" are not added, they will be defined as "N/A." -- **If There is an Error:** - - Message: "Please verify that your input is in the correct format. Include the following details: n/ `` p/ `` e/ `` a/ `
` j/ `` i/ `` [t/ ``] [rn/ ``]." +When reading commands, there are certain syntax conventions that help indicate how to use them: -**Handling Duplicates:** -If a customer with the same name, email, job, and income is already saved, you'll get a message: "This customer is already saved as a contact." +- **`< >` (Angle Brackets):** + Text enclosed in angle brackets represents a **placeholder** for the actual value you need to provide. For example, `` should be replaced by the client's actual name, such as "John Doe." ---- +- **`[ ]` (Square Brackets):** + Components enclosed in square brackets are **optional**. You can choose to include them if necessary, but they are not required for the command to execute. For instance, `[t/ ]` means that the credit card tier is optional, and if omitted, a default value will be used. + +### Example Command: +``` +add n/ p/ e/ a/
j/ i/ [t/ ] [rn/ ] +``` +- **Mandatory Components**: Flags such as `n/`, `p/`, `e/`, `a/`, `j/`, and `i/` must be followed by valid arguments like the name, phone number, and job title. +- **Optional Components**: Flags like `t/` and `rn/` are enclosed in square brackets, indicating they are optional. + +## 5.2 Data Modification Commands + +### 5.2.1 Adding a new client {#add-command} + +**Purpose:** Save detailed records of a new client. + +Each client's record includes their name, contact number, email, occupation, and income. You can also enter the optional fields for credit card tier and remark here. Otherwise, new users are assigned a default value of "N.A". -### Feature 2: Remove Old Customer {#remove-old-customer} +**Command Format:** + ``` + add n/ p/ e/ a/
j/ i/ [t/ ] [rn/ ] + ``` +* Mandatory Fields: `n/`, `p/`, `e/`, `a/`, `j/`, `i/` +* Optional Fields: `t/`, `rn/` -**Purpose:** -This feature allows you to remove records of customers who are no longer using your credit card services. +For detailed explanations of each flag and acceptable arguments, refer to Sections [4.3 Flags](#43-flags) and [4.4 Arguments](#44-arguments) -**How to Use It:** -- **Command Format:** +**Examples:** +- Add new customer (without optional fields): ``` - delete + add n/ JOHN DOE p/ 99007766 e/ mrdoe@ntu.sg a/ com3 j/ doctor i/ 99999 ``` -- **Example:** +- Add new customer with tier and remark: ``` - delete 69 + add n/ JOHN DOE p/ 99007766 e/ mrdoe@ntu.sg a/ com3 j/ doctor i/ 99999 t/ gold rn/ got anger issue ``` -#### Parameters +#### What to Expect +- **On Success:** + - Message: + ``` + New client added: Name: , Phone: , Email: , Address:
, Job: , Income: , Tier: , Remark: . + ``` + - If "Tier" and "Remark" are not provided, they will be set to "N.A." and displayed as such in the success message. -| Parameter | Expected Format | Explanation | -|-----------|-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------| -| INDEX | Integer (1 to the last INDEX) | The INDEX must be a valid integer within the registered range (either the original list or any filtered list after using `filter` command). | +- **On Error** + - Message: + ``` + Please verify that your input is in the correct format. Include the following details: n/ p/ e/ a/
j/ i/ [t/ ] [rn/ ]. + ``` -#### What to Expect -- **If Successful:** - - Message: "Customer `` has been deleted." -- **If There is an Error:** - - Invalid index error message: "No customer with `` exists. Please recheck the index." +> **Note on Duplicates:** +> +> AgentAssist will prevent duplicate entries if a client with the **same name, email, job, and income** is already saved. +> When this happens, you will see the following message: +> +> ``` +> This customer is already saved as a contact. +> ``` +> +> **The duplicate contact will not be saved** to prevent redundancy. +> +> If you need to update details for an existing contact, use the `edit` command instead. +> For more information, see Section [5.2.2 Editing a client](#edit-command). -**Handling Duplicates:** -Since customer INDEX are unique identifiers: -- No two customers can have the same index due to the uniqueness constraint on customer index. -- Even if a customer record appears duplicated due to data file modifications, the system assigns a unique index upon loading the data, preventing actual duplicates in the database. ---- -### Feature 3: Edit the existing customer {#edit-existing-customer} -**Purpose:** -This feature allows users to update the details of an existing customer in the database. All customer information can be modified, including contact details, address, job information, and other relevant data. Additionally, users can either append to or replace existing remarks and adjust the customer's tier status. +### 5.2.2 Edit an Existing Client's Information {#edit-command} -**How to Use It:** -- **Command Format:** - ``` - edit n/ p/ e/ a/
j/ i/ [t/ ] [rn/ ] [ra/ - ] - ``` -**Note that `rn/` and `ra/` cannot be used at the same time.** - - **Examples:** - - Edit only 1 specific field: +**Purpose:** Update the details of an existing client in the database. + +All client information, including contact details, address, job information, and other relevant data, can be modified. You can also append to or replace existing remarks and adjust the client's tier status. + +**Command Format:** +``` +edit n/ p/ e/ a/
j/ i/ [t/ ] [rn/ ] [ra/ ] +``` +- Mandatory Field: `` +- Optional Fields: `n/`, `p/`, `e/`, `a/`, `j/`, `i/`, `t/`, `rn/`, `ra/` +- **Note:** `rn/` (new remark) and `ra/` (append remark) cannot be used simultaneously in a single command. + +For detailed explanations of each flag and acceptable arguments, refer to Sections [4.3 Flags](#43-flags) and [4.4 Arguments](#44-arguments) + +**Examples:** +- Edit only 1 specific field: ``` - edit 69 a/ Ridge View Residential College + edit 12 a/ Ridge View Residential College ``` ``` - edit 69 t/ gold + edit 12 t/ gold ``` - - Edit multiple fields at the same time: + +- Edit multiple fields at the same time: ``` - edit 69 p/ 99887766 e/ mrtan_newemail@ntu.sg j/ unemployed i/ 0 t/ reject + edit 12 p/ 99887766 e/ mrtan_newemail@ntu.sg j/ unemployed i/ 0 t/ reject ``` - - Append new remark onto existing one: +- Append new remark onto existing one: ``` - edit 69 ra/ just know his dad is mr moore + edit 12 ra/ Recently received Gordon E. Moore Award ``` - - Replace all remark(s) with a new remark: +- Replace all remark(s) with a new remark: ``` - edit 69 rn/ ran out of money + edit 69 rn/ Do not call, angry about calls ``` -#### Parameters - -| Parameter | Expected Format | Explanation | -|-----------|--------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------| -| INDEX | Integer (1 to the last INDEX) | The index must be a valid integer within the registered range (either the original list or any filtered list after using `filter` command). | -| NAME | Alphanumeric, capitalized | Names are in block letters for clarity and consistency. | -| PHONE | 8-digit number, starts with 8 or 9 | Ensures the contact number is valid in Singapore. | -| EMAIL | Must include "@" and domain, case insensitive | Verifies that the email address is in a standard format. | -| ADDRESS | Any text, case insensitive | Accepts all addresses without case sensitivity. Addresses can have numbers and symbol alike /. | -| JOB | Any text, case insensitive | Accepts all job titles without case sensitivity. | -| INCOME | Non-negative integers | Only positive numbers or zero are valid for income fields. | -| TIER | [optional] String (gold, silver, bronze, reject) | Defines the specific credit card tier to be assigned or updated. | -| REMARK | [optional] Any string | For both `rn/` and `ra/`, remarks are case-insensitive and can include any textual information. | +**What to Expect:** +- **On Success:** + - Message: + ``` + Customer has been updated successfully. + ``` +- **On Error:** + - Message: + ``` + Failed to update customer . + ``` + +> 💡 **Pro Tip:** +> No need to worry about duplicate indexes—AgentAssist guarantees that every customer has a unique index automatically. -#### What to Expect -- **If Successful:** - - Message: "Customer `` has been updated successfully." -- **If There is an Error:** - - Message: "Failed to update customer ``." -**Handling Duplicates:** -- No two customers can have the same index due to the uniqueness constraint on customer index. ---- -### Feature 4: Find a Customer by Details {#find-a-customer-by-details} +### 5.2.3 Delete an Existing Client {#delete-command} -**Purpose:** -This feature allows users to search for customers by specific details such as name, address, email, phone number, job title, or remarks. +**Purpose:** Remove records of customers who are no longer using your credit card services. -**How to Use It:** -To perform a search, use the `filter` command followed by one or more flags (indicating the fields to search) and the corresponding search terms. +**Command Format:** +``` +delete +``` +* Mandatory Field: `` +* Note: The provided `` must be **greater than 0 and less than the total number of customers in the list**. -Searches are **case-insensitive** and use [**substring-matching**](#substring-matching), **except for [Tier](#filtering-by-tier) and [Income](#filtering-by-income)**, which have their own specific matching criteria detailed below. +For detailed explanations of each flag and acceptable arguments, refer to Sections [4.3 Flags](#43-flags) and [4.4 Arguments](#44-arguments) -- **Command Format:** - ``` - filter / [/ ] - ``` -- **Examples:** - - Filter customers by name: - ``` - filter n/ TAN LESHEW +**Examples:** +- Remove a customer with a specific index (e.g. at index 12): ``` - - Filter customers by job: + delete 12 ``` - filter j/ doctor - ``` - - Filter customers by name, job and remark: + +**What to Expect:** +- **On Success:** + - Message: + ``` + Customer has been deleted. + ``` +- **On Error:** + - Invalid index error message: + ``` + No customer with exists. Please recheck the index. + ``` + +> 💡 **Pro Tip:** +> No need to worry about duplicate indexes—AgentAssist guarantees that every customer has a unique index automatically. + + + + +### 5.2.4 Delete All Existing Clients {#clear-command} + +**Purpose:** Delete all clients from the database, effectively resetting the application’s contact list + +**Command Format:** +``` +clear +``` + +**What to Expect:** +- **On Success:** + - Message: ``` - filter n/ Gordon Moore j/ doctor r/ award winner + Address book has been cleared! ``` + The application will remove all client data from the list, effectively resetting the client database. +- **On Error:** + - This command does not typically produce errors but will have no effect if there are no clients in the database to clear. -#### Parameters - -| Parameter | Expected Format | Explanation | -|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------| -| FLAG | Refer to the list of supported flags detailed below. | Identifies the field to search.

e.g., `n/` for name, `j/` for job. | | -| SEARCH TERM | Follows the syntax for [each field's expected input](#add-command-parameters).

**Income** requires a numeric value with a comparison operator (`=`, `>`, `<`), while **Tier** allows for partial (prefix) matching. Other fields follow substring matching. | The value to search for in the specified field.

e.g., `doctor` for job, `>5000` for income). | - -#### Supported flags: -- `n/` for Name -- `p/` for Phone Number -- `e/` for Email -- `a/` for Address -- `j/` for Job -- `r/` for Remarks -- `t/` for Tier - -#### Substring Matching: -- Substring matching is used for searches, meaning that the search term must match a part of the field in the same order as it appears in the customer record. -- For instance, if a customer’s name is `Gordon Moore`, the search term `Gordon`, `Moore`, or `Gordon Moore` will match, but `Moore Gordon` will not. - -#### Filtering By Tier -- **Prefix Matching:** Tier searches use **prefix matching**, meaning the search term must match the beginning of the tier exactly. - - If a customer has a tier labeled `Gold`, a search for `t/ G` or `t/ Gold` will match, but `t/ ld` or `t/ Gold Premium` will not. - -#### Filtering By Income -- **Comparison Operators:** Filtering by income allows numeric comparisons using operators `=`, `>`, or `<` to find customers whose income meets certain criteria. -- **Equal to (`=`):** Use `=` to find customers with a specific income. - - `i/ =5000` will match customers with an income of exactly 5000. -- **Greater than (`>`):** Use `>` to find customers with an income higher than the specified threshold. - - `i/ >5000` will match customers with incomes greater than 5000. -- **Less than (`<`):** Use `<` to find customers with an income lower than the specified threshold. - - `i/ <5000` will match customers with incomes below 5000. +> ⛔ **Danger:** +> The `clear` command is **irreversible** and does not provide a confirmation message before clearing all records. Once executed, all client data is **permanently deleted**. +> +> It is highly recommended to **avoid using this command** unless absolutely necessary. -#### What to Expect -- **If Successful:** - - Message: "`x` person listed!", where `x` is the number of matching results. -- **If Unsuccessful (No Matches Found):** - - Message: "0 persons listed!" -- **If There is an Error:** - - No Valid Flags Used: - - Message: - - "filter: Searches for all customers whose specified field contains the given substring (case-insensitive) and displays the results in a numbered list. - - Parameters: `/ ` - - Flags: `n/` (name), `p/` (phone), `e/` (email), `a/` (address), `j/` (job), `r/` (remarks) - - Example: `filter n/ Alice p/ 91112222` + + + +## 5.3 Data Filtering Commands + +### 5.3.1 List All Clients {#list-command} + +**Purpose:** View a list of all clients saved in AgentAssist. + +**Command Format:** +``` +list +``` +* No parameters are required for this command. Any parameter added will be ignored. - This will find all customers whose names contain 'Alice' and has phone number '91112222'." - - Search Term Fails to Meet Requirement (i.e. Phone Number longer than 8 digits): - - The system will display usage hints specific to the first invalid search term. ---- -### Feature 5: Help {#help} +### 5.2.3 Filter Clients by Details / Find a Client {#filter-command} -**Purpose:** -This feature provides users with quick access to the command summary and the user guide for the application, helping them understand how to use various features effectively. +**Purpose:** Search for clients by specific details such as name, address, email, phone number, job title, income, or remarks. -**How to Use It:** -- **Command Format:** +**Command Format:** +``` +filter n/ p/ e/ a/
j/ r/ t/ i/ +``` +- **Mandatory Field**: One or more flags with corresponding search terms. +- **Special Syntax for Income (i/)**: + - When filtering by income, use comparison operators `=`, `>`, or `<` to specify criteria. + - Example: `i/ >5000` will filter customers with an income greater than 5000. + - See [Filtering By Income](#filtering-by-income) for more information. + +For detailed explanations of each flag and acceptable arguments, refer to Sections [4.3 Flags](#43-flags) and [4.4 Arguments](#44-arguments) + +**Examples:** +- Filter customers by name: + ``` + filter n/ John Doe ``` - help +- Filter customers by job: ``` -- **Example:** + filter j/ doctor ``` - help +- Filter customers by name, job and remark: ``` + filter n/ Gordon Moore j/ doctor r/ award winner + ``` +**Matching Criteria & Filter Behavior:** + +- **Substring Matching: (For most fields)** + Searches for most fields use **substring matching**, meaning the search term must match part of the field in the same order as it appears in the customer record. + - **Example:** + If a customer’s name is `Gordon Moore`, the search term `Gordon`, `Moore`, or `Gordon Moore` will match, but `Moore Gordon` will not. + +- **Filtering by Tier (Prefix Matching):** + Tier searches use **prefix matching**, meaning the search term must match the beginning of the tier exactly. + - **Example:** + If a customer has a tier labeled `Gold`, a search for `t/ G` or `t/ Gold` will match, but `t/ ld` or `t/ Gold Premium` will not. + +- **Filtering by Income (Using Comparison Operators):** +Filtering by income allows numeric comparisons using operators `=`, `>`, or `<` to find customers whose income meets certain criteria. + + - **Equal to (`=`):** + Use `=` to find customers with a specific income. + Example: `i/ =5000` will match customers with an income of exactly 5000. + + - **Greater than (`>`):** + Use `>` to find customers with an income higher than the specified threshold. + Example: `i/ >5000` will match customers with incomes greater than 5000. + + - **Less than (`<`):** + Use `<` to find customers with an income lower than the specified threshold. + Example: `i/ <5000` will match customers with incomes below 5000. + +**What to Expect:** +- **On Success:** + - Message: + ``` + x person(s) listed! + ``` + where `x` is the number of matching results. +- **On Error:** + - If no valid flags are used: + ``` + filter: Searches for all customers whose specified field contains the given substring (case-insensitive) and displays the results in a numbered list. -#### Parameters -- **Flag:** N/A - - There are no parameters required for this command. + Parameters: / + + Flags: n/ (name), p/ (phone), e/ (email), a/ (address), j/ (job), r/ (remarks) + + Example: filter n/ Alice p/ 91112222 + ``` + - If a search term fails to meet the requirements (e.g., invalid phone number length), the system will display usage hints specific to the first invalid search term. -#### What to Expect -- **If Successful:** - - No immediate message is displayed in the command interface. - - Opens up a dialog box that provides command summary table with command format and basic example, together with the hyperlink to the user guide markdown file, allowing users to easily access detailed instructions and information. -**Handling Duplicates:** -- N/A as this command does not involve processing or displaying data that could involve duplicates. ---- -### Feature 6: Exit {#exit} -**Purpose:** -Allows users to exit the application through a simple command, eliminating the need to use the window's close button or external controls. +## 5.4 General Commands -**How to Use It:** -- **Command Format:** - ``` - exit - ``` -- **Example:** - ``` - exit - ``` +### 5.4.1 Viewing a Client's Details {#view-command} -#### Parameters -- **Flag:** N/A - - No parameters are needed to execute this command. +**Purpose:** View the full details of a selected client by opening a detailed card with all available information about the client. -#### What to Expect -- **If Successful:** - - The message "Terminating program…" is displayed. - - The program will then exit after a short delay, effectively closing the application. +**Methods to View Client Details:** +- **Method 1 - Using GUI:** + Left-click on a specific client in the list to open the client's card and display their full details. +- **Method 2 - Using CLI:** + 1. Use the `↑` and `↓` arrow keys to navigate through the client list in the Command Line Interface (CLI). + 2. Once the desired client is selected **(highlighted in blue)**, type the following command to open their detailed card: + ``` + view + ``` + - This will display the full details of the currently selected client. -**Handling Duplicates:** -- N/A as this command is unique and does not process data that could involve duplicates. +> 💡 **Pro Tip:** +> Using the CLI with the `↑` and `↓` arrow keys to select a client is a fast, keyboard-friendly way to navigate through the list. Once selected, simply type `view` to instantly display the client's details without needing to use the mouse! ---- -### Feature 7: Automatically Save Data {#saving-data} -**Purpose:** -This feature ensures that any details you add to the app are saved automatically. When you close and reopen the app, all your data will still be there. -**How it Works:** -- **Command Format and Example:** Not applicable, as this process is automatic. +### 5.4.2 Closing a Client's Details {#close-command} -#### Parameters -- **Flags and Parameters:** There are no parameters needed for this feature. +**Purpose:** Close the detailed view of a client's card and return to the main client list. -#### What to Expect -- **If Successful:** You can access all the data you've entered previously. -- **If There is an Error:** There's a chance that the data might not be saved due to an error, and you could lose information. +**Command Format:** +``` +close +``` +- If a client's detailed card is currently open, this command will return you to the main list view. +- **Note:** If no client cards are open, the `close` command is ignored. --------------------------------------------------------------------------------------------------------------------- + + + +### 5.4.3 Help Menu {#help-command} + +**Purpose:** Provides quick access to a command summary and the user guide for AgentAssist. + +**Command Format:** +``` +help +``` +- Opens up a dialog box that provides: + - **Command summary table** with command format and basic examples + - **Hyperlink to the User Guide** + + + + +### 5.4.4 Exiting AgentAssist {#exit} + +**Purpose:** Exit the application directly from the command line, providing a quick and easy way to close the program without using external controls. -## FAQ {#faq} +**Command Format:** +``` +exit +``` +- The message `Terminating program…` is displayed. +- After a brief delay, the program will close, effectively exiting the application. -**Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder.
-**Q**: How do I change the remarks or credit card tier of an existing customer?
-**A**: Use the [`edit`](#feature-4-edit-the-existing-customer) command, and specify the corresponding `t/` and or `rn/` or `ra/` flag to change these two fields.
-**Q**: Why am I getting an error when trying to edit the remark of an existing customer?
-**A**: Besides making sure that the command syntax is correct, please note that the `rn/` and `ra/` flags cannot be used together, as `rn/` is used to provide a new remark that will override any existing remark. Whilst, `ra/` will append a given remark to any existing remark. -**Q**: What do the different tier colors represent in the UI? -**A**: Each credit card tier is visually distinguished in the UI: Gold is marked with a gold banner, Silver with a silver banner, Bronze with a bronze banner, and Reject with a red banner. This makes it easy to see at a glance the tier of each customer. +## 5.5 Saving Data +AgentAssist **automatically saves** all client data to your computer after each command. There's no need to manually save anything. + + + + + +## 5.6 Modifying the Data File +The data in AgentAssist is automatically saved as a [JSON](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/JSON) file as `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file. + +> ⚠️ **Danger:** +> If the data file format becomes invalid, AgentAssist will **discard all data** and start with an empty file on the next run. It's strongly recommended to back up the file before any manual edits. +> +> Incorrect data modifications may also cause unexpected behavior. **Only modify the data file if you're confident in doing so correctly.** + + +-------------------------------------------------------------------------------------------------------------------- + +## 6. FAQ + +### How do I transfer my data to another Computer? +Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder. + +### How do I change the remarks or credit card tier of an existing customer? +Use the [`edit` command](#feature-4-edit-the-existing-customer), and specify the `t/` flag for the credit card tier, and `rn/` or `ra/` for remarks. + +### Why am I getting an error when trying to edit the remark of an existing customer? +Ensure that the command syntax is correct, and note that the `rn/` and `ra/` flags cannot be used together. The `rn/` flag replaces the existing remark, while `ra/` appends to the current remark. + +### What do the different tier colors represent in the UI? +Each credit card tier is visually distinguished in the UI: Gold is marked with a gold banner, Silver with a silver banner, Bronze with a bronze banner, and Reject with a red banner. This makes it easy to see at a glance the tier of each customer. -------------------------------------------------------------------------------------------------------------------- -## Known issues {#known-issues} +## 7. Known issues 1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again. 2. **If you minimize the Help Window** and then run the `help` command (or use the `Help` menu, or the keyboard shortcut `F1`) again, the original Help Window will remain minimized, and no new Help Window will appear. The remedy is to manually restore the minimized Help Window. -------------------------------------------------------------------------------------------------------------------- -## Command Summary {#command-summary} - -| Action | Command Format | Example | -|--------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------| -| **Save Data Automatically** | *Automatic* | *No command required* | -| **Add New Customer** | `add n/ p/ e/ a/
j/ i/ [t/ ] [rn/ ]` | `add n/ TAN LESHEW p/ 99007766 e/ mrtan@ntu.sg a/ com3 j/ doctor i/ 99999 t/ gold rn/ got anger issue` | -| **Remove Old Customer** | `delete ` | `delete 69` | -| **Edit Existing Customer** | `edit n/ p/ e/ a/
j/ i/ [t/ ] [rn/ ] [ra/ / ` | `filter n/ TAN LESHEW` | -| **Help** | `help` | `help` | -| **Exit** | `exit` | `exit` | +## 8. Command Summary + +| **Action** | **Command Format** | **Example** | +|----------------------------|------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------| +| **Add New Client** | `add n/ p/ e/ a/
j/ i/ [t/] [rn/]` | `add n/ GORDON MOORE p/ 99007766 e/ gmoore@ntu.sg a/ COM3 j/ engineer i/ 99999 t/ gold rn/ remark` | +| **Delete Existing Client** | `delete ` | `delete 69` | +| **Edit Existing Client** | `edit n/ p/ e/ a/
j/ i/ [t/] [rn/] [ra/]` | `edit 69 n/ GORDON MOORE p/ 77337733 e/ gmoore_new@ntu.sg a/ COM3 j/ doctor i/ 1000000000 ra/ added info` | +| **List All Clients** | `list` | `list` | +| **Filter Client List** | `filter n/ p/ e/ a/
j/ r/ t/ i/` | `filter n/ GORDON MOORE j/ doctor t/ gold` | +| **View Client Details** | `view` | `view` | +| **Close Client Details** | `close` | `close` | +| **View Help** | `help` | `help` | +| **Exit Application** | `exit` | `exit` | +| **Clear All Data** | `clear` | `clear` | + + + From 7430457308716addc81e85f167bf342ff06572b6 Mon Sep 17 00:00:00 2001 From: leyew <102467346+itsme-zeix@users.noreply.github.com> Date: Mon, 21 Oct 2024 22:39:59 +0800 Subject: [PATCH 10/45] Add Prerequisite section and setup congrats message --- docs/UserGuide.md | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index d98794846ca..80be6374415 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -19,16 +19,7 @@ Let’s begin and get you up to speed with AgentAssist! # Table of Contents 1. [Introduction](#1-introduction) -
- Subsections - - 1.1 [What is AgentAssist?](#11-what-is-agentassist) - 1.2 [Why use AgentAssist?](#12-why-use-agentassist) - -
- 2. [Important Prerequisites](#2-important-prerequisites) - 3. [Getting Started](#3-getting-started)
Subsections @@ -87,12 +78,24 @@ AgentAssist is the **definitive desktop tool for credit card sales agents**. Mer Maximize your productivity, minimize your response time, and amplify your sales performance. With AgentAssist, you're not just keeping up with the competitive world of credit card sales — _you're setting the pace_. -## 1.2 Why use AgentAssist? - -------------------------------------------------------------------------------------------------------------------- # 2. Important Prerequisites +Before you start using AgentAssist, there are a few prerequisites to ensure you get the most out of the application: + +### Familiarity with Keyboard Navigation +AgentAssist is designed to enhance speed and efficiency, with a strong focus on **keyboard-based navigation**. While the application includes a Graphical User Interface (GUI), its full potential is unlocked when you use **keyboard commands**. Therefore, it is important to: + +- Be comfortable navigating applications using the keyboard. +- Familiarize yourself with basic `Command Line Interface (CLI)` commands if you haven't already. This will make it easier to use AgentAssist’s command system effectively. +- Know common keyboard shortcuts (e.g., `Enter`, `Arrow keys`, etc.). + +### Basic Understanding of Data Fields +AgentAssist allows you to manage client data like names, phone numbers, emails, and job information. A basic understanding of these data fields will make it easier to add, edit, and filter client information. + +🎉 **By meeting these prerequisites, you'll be ready to make the most of AgentAssist’s fast, keyboard-driven interface and powerful data management features.** 🎉 + # 3. Getting Started Welcome to AgentAssist. Here’s how to get up and running quickly and easily. @@ -154,6 +157,8 @@ Move the .jar file you downloaded into this folder. - A window similar to the below image should appear in a few seconds. You will see a graphical user interface with sample contact information already added.

![Ui](images/Ui.png) +4. 🎉 **Congratulations! AgentAssist is now up and running!** 🎉 + You're all set to start using AgentAssist to manage your contacts, track your sales, and boost your productivity! ## 3.2 Graphical User Interface (GUI) Layout From c3aac31f81298cbee09207c8b65654399aa407a1 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 05:03:24 +0800 Subject: [PATCH 11/45] Add view command --- .../address/logic/commands/ViewCommand.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/main/java/seedu/address/logic/commands/ViewCommand.java diff --git a/src/main/java/seedu/address/logic/commands/ViewCommand.java b/src/main/java/seedu/address/logic/commands/ViewCommand.java new file mode 100644 index 00000000000..47b89e42486 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ViewCommand.java @@ -0,0 +1,51 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; + +import java.util.List; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.Person; + +public class ViewCommand extends Command { + + public static final String COMMAND_WORD = "view"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Views the person identified 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_VIEW_PERSON_SUCCESS = "Viewed Person: %1$s"; + + private final Index targetIndex; + + public ViewCommand(Index targetIndex) { + requireNonNull(targetIndex); + this.targetIndex = targetIndex; + } + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + List lastShownList = model.getFilteredPersonList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToView = lastShownList.get(targetIndex.getZeroBased()); + return new CommandResult(String.format(MESSAGE_VIEW_PERSON_SUCCESS, personToView), + false, false, true, personToView); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ViewCommand // instanceof handles nulls + && targetIndex.equals(((ViewCommand) other).targetIndex)); // state check + } +} \ No newline at end of file From 2bb50ac0f6fd6ea5d6d91ce22491490c47190bff Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 05:03:32 +0800 Subject: [PATCH 12/45] Add view command parser --- .../logic/parser/ViewCommandParser.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/java/seedu/address/logic/parser/ViewCommandParser.java diff --git a/src/main/java/seedu/address/logic/parser/ViewCommandParser.java b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java new file mode 100644 index 00000000000..9cba7a182a2 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java @@ -0,0 +1,28 @@ +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.ViewCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new ViewCommand object + */ +public class ViewCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ViewCommand + * and returns a ViewCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ViewCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new ViewCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE), pe); + } + } +} \ No newline at end of file From b414167d4065ce0067851caadaadd7d714edef48 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 05:03:54 +0800 Subject: [PATCH 13/45] Update parser related file --- .../java/seedu/address/logic/parser/AddressBookParser.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index bc2f04c5ab5..9289d33708e 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -17,6 +17,7 @@ import seedu.address.logic.commands.FilterCommand; import seedu.address.logic.commands.HelpCommand; import seedu.address.logic.commands.ListCommand; +import seedu.address.logic.commands.ViewCommand; import seedu.address.logic.parser.exceptions.ParseException; /** @@ -78,6 +79,9 @@ public Command parseCommand(String userInput) throws ParseException { case HelpCommand.COMMAND_WORD: return new HelpCommand(); + 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); From 0e84a175ef59b02c14c21fafe3341cc4b1063f96 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 05:04:22 +0800 Subject: [PATCH 14/45] Refactor command classes as we going to define which person we are viewing with view command --- src/main/java/seedu/address/logic/commands/ExitCommand.java | 2 +- src/main/java/seedu/address/logic/commands/HelpCommand.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java index 3dd85a8ba90..d755ce8d850 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java @@ -13,7 +13,7 @@ public class ExitCommand extends Command { @Override public CommandResult execute(Model model) { - return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false, null); } } diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java index bf824f91bd0..cca5cd03ac5 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java @@ -16,6 +16,6 @@ public class HelpCommand extends Command { @Override public CommandResult execute(Model model) { - return new CommandResult(SHOWING_HELP_MESSAGE, true, false); + return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false, null); } } From e4c4ea361a207fcc5055d21453b1c1fac47b5575 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 05:04:43 +0800 Subject: [PATCH 15/45] Update command result to include view command --- .../address/logic/commands/CommandResult.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java index 249b6072d0d..384323fe2cb 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/address/logic/commands/CommandResult.java @@ -5,6 +5,7 @@ import java.util.Objects; import seedu.address.commons.util.ToStringBuilder; +import seedu.address.model.person.Person; /** * Represents the result of a command execution. @@ -19,13 +20,18 @@ public class CommandResult { /** The application should exit. */ private final boolean exit; + private final boolean showPerson; + private final Person viewedPerson; + /** * Constructs a {@code CommandResult} with the specified fields. */ - public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { - this.feedbackToUser = requireNonNull(feedbackToUser); + public CommandResult(String feedbackToUser, boolean showHelp, boolean exit, boolean showPerson, Person viewedPerson) { + this.feedbackToUser = feedbackToUser; this.showHelp = showHelp; this.exit = exit; + this.showPerson = showPerson; + this.viewedPerson = viewedPerson; } /** @@ -33,7 +39,7 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { * and other fields set to their default value. */ public CommandResult(String feedbackToUser) { - this(feedbackToUser, false, false); + this(feedbackToUser, false, false, false, null); } public String getFeedbackToUser() { @@ -48,6 +54,14 @@ public boolean isExit() { return exit; } + public boolean isShowPerson() { + return showPerson; + } + + public Person getViewedPerson() { + return viewedPerson; + } + @Override public boolean equals(Object other) { if (other == this) { From 0e89fdd7aead3e32928788a9e2cbb635356db1ad Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 05:05:18 +0800 Subject: [PATCH 16/45] Update model classes --- src/main/java/seedu/address/model/Model.java | 19 +++++++- .../seedu/address/model/ModelManager.java | 44 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index 59747e3d623..5b44a4ef7a1 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -3,6 +3,7 @@ import java.nio.file.Path; import java.util.function.Predicate; +import javafx.beans.property.ReadOnlyProperty; import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; import seedu.address.model.person.Person; @@ -84,4 +85,20 @@ public interface Model { * @throws NullPointerException if {@code predicates} is null. */ void updateFilteredPersonList(Predicate predicate); -} + + /** + * Selected person in the filtered person list. + * null if no person is selected. + */ + ReadOnlyProperty selectedPersonProperty(); + + /** + * Returns the selected person in the filtered person list. + * null if no person is selected. + */ + Person getSelectedPerson(); + + /** + * Sets the selected person in the filtered person list. + */ + void setSelectedPerson(Person person);} diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 57bc563fde6..2b2a7543fa1 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -7,11 +7,15 @@ import java.util.function.Predicate; import java.util.logging.Logger; +import javafx.beans.property.ReadOnlyProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; import seedu.address.model.person.Person; +import seedu.address.model.person.exceptions.PersonNotFoundException; /** * Represents the in-memory model of the address book data. @@ -22,11 +26,13 @@ public class ModelManager implements Model { private final AddressBook addressBook; private final UserPrefs userPrefs; private final FilteredList filteredPersons; + private final SimpleObjectProperty selectedPerson = new SimpleObjectProperty<>(); /** * Initializes a ModelManager with the given addressBook and userPrefs. */ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { + super(); requireAllNonNull(addressBook, userPrefs); logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); @@ -40,6 +46,7 @@ public ModelManager() { this(new AddressBook(), new UserPrefs()); } + //=========== UserPrefs ================================================================================== @Override @@ -111,6 +118,43 @@ public void setPerson(Person target, Person editedPerson) { addressBook.setPerson(target, editedPerson); } + //=========== Selected Person =========================================================================== + + @Override + public ReadOnlyProperty selectedPersonProperty() { + return selectedPerson; + } + + @Override + public Person getSelectedPerson() { + return selectedPerson.getValue(); + } + + @Override + public void setSelectedPerson(Person person) { + if (person != null && !filteredPersons.contains(person)) { + throw new PersonNotFoundException(); + } + selectedPerson.setValue(person); + } + + /** + * Ensures {@code selectedPerson} is a valid person in {@code filteredPersons}. + */ + private void ensureSelectedPersonIsValid(ListChangeListener.Change change) { + while (change.next()) { + if (selectedPerson.getValue() == null) { + return; + } + + boolean wasSelectedPersonRemoved = change.getRemoved().stream() + .anyMatch(removedPerson -> selectedPerson.getValue().isSamePerson(removedPerson)); + if (wasSelectedPersonRemoved) { + selectedPerson.setValue(change.getFrom() > 0 ? filteredPersons.get(change.getFrom() - 1) : null); + } + } + } + //=========== Filtered Person List Accessors ============================================================= /** From 9cb5b265b38c7fe8cb0296a8bb8e6b5d5cf20d7e Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 05:05:32 +0800 Subject: [PATCH 17/45] Add split screen panel --- .../seedu/address/ui/PersonDetailPanel.java | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/main/java/seedu/address/ui/PersonDetailPanel.java diff --git a/src/main/java/seedu/address/ui/PersonDetailPanel.java b/src/main/java/seedu/address/ui/PersonDetailPanel.java new file mode 100644 index 00000000000..7ba03b49f81 --- /dev/null +++ b/src/main/java/seedu/address/ui/PersonDetailPanel.java @@ -0,0 +1,106 @@ +package seedu.address.ui; + +import java.util.logging.Logger; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.Region; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.person.Person; + +public class PersonDetailPanel extends UiPart { + private static final String FXML = "PersonDetailPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(PersonDetailPanel.class); + + @FXML + private Label nameLabel; + @FXML + private Label phoneLabel; + @FXML + private Label addressLabel; + @FXML + private Label emailLabel; + @FXML + private Label jobLabel; + @FXML + private Label incomeLabel; + @FXML + private FlowPane tierPane; + @FXML + private Label remarkLabel; + + public PersonDetailPanel() { + super(FXML); + initializeLabels(); + hideAllFields(); + } + + private void initializeLabels() { + nameLabel = nameLabel == null ? new Label() : nameLabel; + phoneLabel = phoneLabel == null ? new Label() : phoneLabel; + addressLabel = addressLabel == null ? new Label() : addressLabel; + emailLabel = emailLabel == null ? new Label() : emailLabel; + jobLabel = jobLabel == null ? new Label() : jobLabel; + incomeLabel = incomeLabel == null ? new Label() : incomeLabel; + remarkLabel = remarkLabel == null ? new Label() : remarkLabel; + } + + private void hideAllFields() { + setManagedAndVisible(nameLabel, false); + setManagedAndVisible(phoneLabel, false); + setManagedAndVisible(addressLabel, false); + setManagedAndVisible(emailLabel, false); + setManagedAndVisible(jobLabel, false); + setManagedAndVisible(incomeLabel, false); + setManagedAndVisible(tierPane, false); + setManagedAndVisible(remarkLabel, false); + } + + private void setManagedAndVisible(javafx.scene.Node node, boolean value) { + if (node != null) { + node.setManaged(value); + node.setVisible(value); + } + } + + public void setPersonDetails(Person person) { + if (person != null) { + showAllFields(); + setLabelText(nameLabel, person.getName().fullName); + setLabelText(phoneLabel, person.getPhone().value); + setLabelText(addressLabel, person.getAddress().value); + setLabelText(emailLabel, person.getEmail().value); + setLabelText(jobLabel, person.getJob().value); + setLabelText(incomeLabel, String.valueOf(person.getIncome())); + setTier(person.getTier().toParsableString()); + setLabelText(remarkLabel, person.getRemark().value); + } else { + hideAllFields(); + } + } + + private void setLabelText(Label label, String text) { + if (label != null) { + label.setText(text); + } + } + + private void setTier(String tier) { + tierPane.getChildren().clear(); + Label tierLabel = new Label(tier.toUpperCase()); + tierLabel.getStyleClass().addAll("label", "detail-tier-label", tier.toLowerCase() + "-tier"); + tierPane.getChildren().add(tierLabel); + } + + private void showAllFields() { + setManagedAndVisible(nameLabel, true); + setManagedAndVisible(phoneLabel, true); + setManagedAndVisible(addressLabel, true); + setManagedAndVisible(emailLabel, true); + setManagedAndVisible(jobLabel, true); + setManagedAndVisible(incomeLabel, true); + setManagedAndVisible(tierPane, true); + setManagedAndVisible(remarkLabel, true); + } +} \ No newline at end of file From 86e9eebdfd62e86b5b6599be52256e1b0d9c52a1 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 05:05:50 +0800 Subject: [PATCH 18/45] Link new panel with main window --- .../java/seedu/address/ui/MainWindow.java | 86 ++++++++++++------- .../java/seedu/address/ui/PersonCard.java | 24 +++--- .../seedu/address/ui/PersonListPanel.java | 4 + 3 files changed, 69 insertions(+), 45 deletions(-) diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 2d5a689f135..51e6bc74a78 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -6,9 +6,11 @@ import javafx.fxml.FXML; import javafx.scene.control.MenuItem; import javafx.scene.control.TextInputControl; +import javafx.scene.image.ImageView; import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; import javafx.scene.layout.StackPane; +import javafx.scene.control.SplitPane; import javafx.stage.Stage; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; @@ -16,6 +18,7 @@ import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Person; /** * The Main Window. Provides the basic application layout containing @@ -30,8 +33,8 @@ public class MainWindow extends UiPart { private Stage primaryStage; private Logic logic; - // Independent Ui parts residing in this Ui container private PersonListPanel personListPanel; + private PersonDetailPanel personDetailPanel; private ResultDisplay resultDisplay; private HelpWindow helpWindow; @@ -41,30 +44,30 @@ public class MainWindow extends UiPart { @FXML private MenuItem helpMenuItem; + @FXML + private SplitPane splitPane; + @FXML private StackPane personListPanelPlaceholder; + @FXML + private StackPane personDetailsPanelPlaceholder; + @FXML private StackPane resultDisplayPlaceholder; @FXML private StackPane statusbarPlaceholder; - /** - * Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}. - */ + @FXML + private ImageView placeholderImage; + public MainWindow(Stage primaryStage, Logic logic) { super(FXML, primaryStage); - - // Set dependencies this.primaryStage = primaryStage; this.logic = logic; - - // Configure the UI setWindowDefaultSize(logic.getGuiSettings()); - setAccelerators(); - helpWindow = new HelpWindow(); } @@ -83,21 +86,6 @@ private void setAccelerators() { private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { menuItem.setAccelerator(keyCombination); - /* - * TODO: the code below can be removed once the bug reported here - * https://bugs.openjdk.java.net/browse/JDK-8131666 - * is fixed in later version of SDK. - * - * According to the bug report, TextInputControl (TextField, TextArea) will - * consume function-key events. Because CommandBox contains a TextField, and - * ResultDisplay contains a TextArea, thus some accelerators (e.g F1) will - * not work when the focus is in them because the key event is consumed by - * the TextInputControl(s). - * - * For now, we add following event filter to capture such key events and open - * help window purposely so to support accelerators even when focus is - * in CommandBox or ResultDisplay. - */ getRoot().addEventFilter(KeyEvent.KEY_PRESSED, event -> { if (event.getTarget() instanceof TextInputControl && keyCombination.match(event)) { menuItem.getOnAction().handle(new ActionEvent()); @@ -113,6 +101,9 @@ void fillInnerParts() { personListPanel = new PersonListPanel(logic.getFilteredPersonList()); personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + personDetailPanel = new PersonDetailPanel(); + personDetailsPanelPlaceholder.getChildren().add(personDetailPanel.getRoot()); + resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); @@ -121,6 +112,14 @@ void fillInnerParts() { CommandBox commandBox = new CommandBox(this::executeCommand); commandBoxPlaceholder.getChildren().add(commandBox.getRoot()); + + splitPane.setDividerPositions(0.6); + + splitPane.getDividers().get(0).positionProperty().addListener((observable, oldValue, newValue) -> { + splitPane.setDividerPositions(0.6); + }); + + splitPane.getItems().remove(personDetailsPanelPlaceholder); } /** @@ -163,8 +162,29 @@ private void handleExit() { primaryStage.hide(); } - public PersonListPanel getPersonListPanel() { - return personListPanel; + /** + * Handles the view command to display person details. + */ + private void handleViewCommand(String commandText) { + if (!splitPane.getItems().contains(personDetailsPanelPlaceholder)) { + splitPane.getItems().add(personDetailsPanelPlaceholder); + placeholderImage.setVisible(false); + splitPane.setDividerPositions(0.6); + } + + String[] commandParts = commandText.split("\\s+"); + if (commandParts.length > 1) { + try { + int index = Integer.parseInt(commandParts[1]) - 1; // Assuming 1-based indexing in UI + Person personToView = logic.getFilteredPersonList().get(index); + personDetailPanel.setPersonDetails(personToView); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + // Handle invalid index + resultDisplay.setFeedbackToUser("Invalid person index."); + } + } else { + resultDisplay.setFeedbackToUser("Please provide a person index to view."); + } } /** @@ -175,10 +195,13 @@ public PersonListPanel getPersonListPanel() { private CommandResult executeCommand(String commandText) throws CommandException, ParseException { try { CommandResult commandResult = logic.execute(commandText); - logger.info("Result: " + commandResult.getFeedbackToUser()); // used to log the result, not actually - // used to display + logger.info("Result: " + commandResult.getFeedbackToUser()); resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser()); + if (commandText.trim().toLowerCase().startsWith("view")) { + handleViewCommand(commandText); + } + if (commandResult.isShowHelp()) { handleHelp(); } @@ -189,9 +212,10 @@ private CommandResult executeCommand(String commandText) throws CommandException return commandResult; } catch (CommandException | ParseException e) { - logger.info("An error occurred while executing command: " + commandText); + logger.info("Invalid command: " + commandText); resultDisplay.setFeedbackToUser(e.getMessage()); throw e; } } -} + +} \ No newline at end of file diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java index 2a0fd281d7e..00aea43f39b 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/seedu/address/ui/PersonCard.java @@ -65,20 +65,16 @@ private void createFields() { private void createTier() { // Create a label for the tier - Label tierLabel = new Label(person.getTier().toParsableString()); + Label tierLabel = new Label(person.getTier().toParsableString().toUpperCase()); - // Apply a different style class based on the tier value - String tier = person.getTier().toParsableString().toUpperCase(); - switch (tier) { - case "GOLD" -> tierLabel.getStyleClass().add("gold-tier"); - case "SILVER" -> tierLabel.getStyleClass().add("silver-tier"); - case "BRONZE" -> tierLabel.getStyleClass().add("bronze-tier"); - case "REJECT" -> tierLabel.getStyleClass().add("reject-tier"); - default -> tierLabel = null; - } - if (tierLabel != null) { - // Add the label to the FlowPane - assignedTier.getChildren().add(tierLabel); - } + // Apply the existing style classes + tierLabel.getStyleClass().add("label"); + + // Add the tier-specific style class + String tier = person.getTier().toParsableString().toLowerCase(); + tierLabel.getStyleClass().add(tier + "-tier"); + + // Add the label to the FlowPane + assignedTier.getChildren().add(tierLabel); } } diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java index f4c501a897b..f52048b99af 100644 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ b/src/main/java/seedu/address/ui/PersonListPanel.java @@ -29,6 +29,10 @@ public PersonListPanel(ObservableList personList) { personListView.setCellFactory(listView -> new PersonListViewCell()); } + public ListView getListView() { + return personListView; + } + /** * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}. */ From 09956988e56cb46d749f802aa2fb88cdc25602ea Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 05:06:04 +0800 Subject: [PATCH 19/45] Update styling files --- src/main/resources/view/DarkTheme.css | 53 ++++++++++++++++++ src/main/resources/view/MainWindow.fxml | 26 ++++++--- .../resources/view/PersonDetailPanel.fxml | 55 +++++++++++++++++++ 3 files changed, 126 insertions(+), 8 deletions(-) create mode 100644 src/main/resources/view/PersonDetailPanel.fxml diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index a299de39ecd..72f58f46d78 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -307,6 +307,47 @@ -fx-padding: 8 1 8 1; } +.cell_small_label { + -fx-font-family: "Segoe UI"; + -fx-font-size: 13px; + -fx-text-fill: #a0a0a0; +} + +.cell_big_label { + -fx-font-family: "Segoe UI Bold"; + -fx-font-size: 16px; + -fx-text-fill: white; +} + +.name-label { + -fx-font-weight: bold; +} + +.detail-tier-label { + -fx-text-fill: white; + -fx-padding: 2 5 2 5; + -fx-border-radius: 3; + -fx-background-radius: 3; + -fx-font-size: 12px; +} + +/* Tier-specific styles for the detail panel */ +.detail-tier-label.gold-tier { + -fx-background-color: #B59410; +} + +.detail-tier-label.silver-tier { + -fx-background-color: #71706e; +} + +.detail-tier-label.bronze-tier { + -fx-background-color: #804a00; +} + +.detail-tier-label.reject-tier { + -fx-background-color: #8B0000; +} + #cardPane { -fx-background-color: transparent; -fx-border-width: 0; @@ -370,3 +411,15 @@ -fx-background-radius: 2; -fx-font-size: 11; } + +#personDetailsPanelPlaceholder { + -fx-background-color: #383838; +} + +.scroll-pane { + -fx-background-color: #383838; +} + +.scroll-pane > .viewport { + -fx-background-color: #383838; +} \ No newline at end of file diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index 8f953aa9bdb..657e86a373d 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -8,6 +8,7 @@ + @@ -46,15 +47,24 @@ - - - - - - - + + + + + + + + + + + + + + + + - + \ No newline at end of file diff --git a/src/main/resources/view/PersonDetailPanel.fxml b/src/main/resources/view/PersonDetailPanel.fxml new file mode 100644 index 00000000000..956f2cecbb4 --- /dev/null +++ b/src/main/resources/view/PersonDetailPanel.fxml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 9c504cedb077934261fd50fed4fd8b3872eff332 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 05:06:20 +0800 Subject: [PATCH 20/45] Update arguments for view person in related tests --- .../address/logic/commands/AddCommandTest.java | 16 ++++++++++++++++ .../logic/commands/CommandResultTest.java | 10 +++++----- .../address/logic/commands/ExitCommandTest.java | 2 +- .../address/logic/commands/HelpCommandTest.java | 2 +- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java index 90e8253f48e..31b3d9438a8 100644 --- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/AddCommandTest.java @@ -14,6 +14,7 @@ import org.junit.jupiter.api.Test; +import javafx.beans.property.ReadOnlyProperty; import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; import seedu.address.logic.Messages; @@ -157,6 +158,21 @@ public ObservableList getFilteredPersonList() { public void updateFilteredPersonList(Predicate predicate) { throw new AssertionError("This method should not be called."); } + + @Override + public ReadOnlyProperty selectedPersonProperty() { + throw new AssertionError("This method should not be called."); + } + + @Override + public Person getSelectedPerson() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setSelectedPerson(Person person) { + throw new AssertionError("This method should not be called."); + } } /** diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/seedu/address/logic/commands/CommandResultTest.java index 7b8c7cd4546..0ed34a91bcc 100644 --- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java +++ b/src/test/java/seedu/address/logic/commands/CommandResultTest.java @@ -14,7 +14,7 @@ public void equals() { // same values -> returns true assertTrue(commandResult.equals(new CommandResult("feedback"))); - assertTrue(commandResult.equals(new CommandResult("feedback", false, false))); + assertTrue(commandResult.equals(new CommandResult("feedback", false, false, false, null))); // same object -> returns true assertTrue(commandResult.equals(commandResult)); @@ -29,10 +29,10 @@ public void equals() { assertFalse(commandResult.equals(new CommandResult("different"))); // different showHelp value -> returns false - assertFalse(commandResult.equals(new CommandResult("feedback", true, false))); + assertFalse(commandResult.equals(new CommandResult("feedback", true, false, false, null))); // different exit value -> returns false - assertFalse(commandResult.equals(new CommandResult("feedback", false, true))); + assertFalse(commandResult.equals(new CommandResult("feedback", false, true, false, null))); } @Test @@ -46,10 +46,10 @@ public void hashcode() { assertNotEquals(commandResult.hashCode(), new CommandResult("different").hashCode()); // different showHelp value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false).hashCode()); + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false, false, null).hashCode()); // different exit value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true).hashCode()); + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true, false, null).hashCode()); } @Test diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java index 9533c473875..19442f05612 100644 --- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java @@ -14,7 +14,7 @@ public class ExitCommandTest { @Test public void execute_exit_success() { - CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false, null); assertCommandSuccess(new ExitCommand(), model, expectedCommandResult, expectedModel); } } diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java index 4904fc4352e..75d8f049870 100644 --- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java @@ -14,7 +14,7 @@ public class HelpCommandTest { @Test public void execute_help_success() { - CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false); + CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false, false, null); assertCommandSuccess(new HelpCommand(), model, expectedCommandResult, expectedModel); } } From 294b216ad1d0251963b81fad3e123d2b2ec06498 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 05:06:29 +0800 Subject: [PATCH 21/45] Add tests for view command --- .../logic/commands/ViewCommandTest.java | 78 +++++++++++++++++++ .../logic/parser/ViewCommandParserTest.java | 30 +++++++ 2 files changed, 108 insertions(+) create mode 100644 src/test/java/seedu/address/logic/commands/ViewCommandTest.java create mode 100644 src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java diff --git a/src/test/java/seedu/address/logic/commands/ViewCommandTest.java b/src/test/java/seedu/address/logic/commands/ViewCommandTest.java new file mode 100644 index 00000000000..c170cba0f48 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/ViewCommandTest.java @@ -0,0 +1,78 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.*; +import static seedu.address.logic.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.person.Person; + +class ViewCommandTest { + + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + + @Test + public void constructor_nullIndex_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new ViewCommand(null)); + } + + @Test + public void execute_validIndexUnfilteredList_success() throws Exception { + Person personToView = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + ViewCommand viewCommand = new ViewCommand(INDEX_FIRST_PERSON); + + String expectedMessage = String.format(ViewCommand.MESSAGE_VIEW_PERSON_SUCCESS, personToView); + + CommandResult result = viewCommand.execute(model); + + assertEquals(expectedMessage, result.getFeedbackToUser()); + assertTrue(result.isShowPerson()); + assertEquals(personToView, result.getViewedPerson()); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); + ViewCommand viewCommand = new ViewCommand(outOfBoundIndex); + + assertThrows(CommandException.class, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX, () -> + viewCommand.execute(model)); + } + + @Test + public void equals() { + ViewCommand viewFirstCommand = new ViewCommand(INDEX_FIRST_PERSON); + ViewCommand viewSecondCommand = new ViewCommand(Index.fromOneBased(2)); + + // same object -> returns true + assertTrue(viewFirstCommand.equals(viewFirstCommand)); + + // same values -> returns true + ViewCommand viewFirstCommandCopy = new ViewCommand(INDEX_FIRST_PERSON); + assertTrue(viewFirstCommand.equals(viewFirstCommandCopy)); + + // different types -> returns false + assertFalse(viewFirstCommand.equals(1)); + + // null -> returns false + assertFalse(viewFirstCommand.equals(null)); + + // different person -> returns false + assertFalse(viewFirstCommand.equals(viewSecondCommand)); + } + + @Test + public void testCommandUsageMessage() { + assertEquals("view: Views the person identified by the index number used in the displayed person list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: view 1", ViewCommand.MESSAGE_USAGE); + } +} \ No newline at end of file diff --git a/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java new file mode 100644 index 00000000000..9cb67240013 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java @@ -0,0 +1,30 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.ViewCommand; + +public class ViewCommandParserTest { + + private ViewCommandParser parser = new ViewCommandParser(); + + @Test + public void parse_validArgs_returnsViewCommand() { + assertParseSuccess(parser, "1", new ViewCommand(INDEX_FIRST_PERSON)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, "", String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE)); + } +} \ No newline at end of file From 2ff0865822eee377d1b276bffc4c7211884ed444 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 05:16:10 +0800 Subject: [PATCH 22/45] Formatting main --- .../address/logic/commands/CommandResult.java | 5 +- .../address/logic/commands/ViewCommand.java | 33 +++++++++++-- .../logic/parser/ViewCommandParser.java | 2 +- src/main/java/seedu/address/model/Model.java | 3 +- .../java/seedu/address/ui/MainWindow.java | 29 ++++++++++-- .../seedu/address/ui/PersonDetailPanel.java | 47 ++++++++++++++++++- 6 files changed, 105 insertions(+), 14 deletions(-) diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java index 384323fe2cb..06b5f7f446a 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/address/logic/commands/CommandResult.java @@ -1,7 +1,5 @@ package seedu.address.logic.commands; -import static java.util.Objects.requireNonNull; - import java.util.Objects; import seedu.address.commons.util.ToStringBuilder; @@ -26,7 +24,8 @@ public class CommandResult { /** * Constructs a {@code CommandResult} with the specified fields. */ - public CommandResult(String feedbackToUser, boolean showHelp, boolean exit, boolean showPerson, Person viewedPerson) { + public CommandResult(String feedbackToUser, boolean showHelp, boolean exit, + boolean showPerson, Person viewedPerson) { this.feedbackToUser = feedbackToUser; this.showHelp = showHelp; this.exit = exit; diff --git a/src/main/java/seedu/address/logic/commands/ViewCommand.java b/src/main/java/seedu/address/logic/commands/ViewCommand.java index 47b89e42486..c5975f8eb1e 100644 --- a/src/main/java/seedu/address/logic/commands/ViewCommand.java +++ b/src/main/java/seedu/address/logic/commands/ViewCommand.java @@ -10,6 +10,10 @@ import seedu.address.model.Model; import seedu.address.model.person.Person; +/** + * Represents a command to view the details of a person identified by their index in the displayed list. + * This command allows users to see detailed information about a specific person in the address book. + */ public class ViewCommand extends Command { public static final String COMMAND_WORD = "view"; @@ -23,10 +27,25 @@ public class ViewCommand extends Command { private final Index targetIndex; + /** + * Creates a new ViewCommand to view the person at the specified {@code targetIndex}. + * + * @param targetIndex The index of the person to view in the filtered person list + * @throws NullPointerException if {@code targetIndex} is null + */ public ViewCommand(Index targetIndex) { requireNonNull(targetIndex); this.targetIndex = targetIndex; } + + /** + * Executes the view command to show the person at the specified index. + * + * @param model The model containing the person data + * @return A CommandResult containing the viewed person's information + * @throws CommandException if the index is invalid or out of bounds + * @throws NullPointerException if {@code model} is null + */ @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); @@ -42,10 +61,16 @@ public CommandResult execute(Model model) throws CommandException { false, false, true, personToView); } + /** + * Compares this ViewCommand to another object for equality. + * + * @param other The object to compare to + * @return true if the other object is also a ViewCommand targeting the same index + */ @Override public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof ViewCommand // instanceof handles nulls - && targetIndex.equals(((ViewCommand) other).targetIndex)); // state check + return other == this + || (other instanceof ViewCommand + && targetIndex.equals(((ViewCommand) other).targetIndex)); } -} \ No newline at end of file +} diff --git a/src/main/java/seedu/address/logic/parser/ViewCommandParser.java b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java index 9cba7a182a2..2d82dbf1ce8 100644 --- a/src/main/java/seedu/address/logic/parser/ViewCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java @@ -25,4 +25,4 @@ public ViewCommand parse(String args) throws ParseException { String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE), pe); } } -} \ No newline at end of file +} diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index 5b44a4ef7a1..b9c7d1ea542 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -101,4 +101,5 @@ public interface Model { /** * Sets the selected person in the filtered person list. */ - void setSelectedPerson(Person person);} + void setSelectedPerson(Person person); +} diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 51e6bc74a78..a1d8773c84c 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -5,12 +5,12 @@ import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.MenuItem; +import javafx.scene.control.SplitPane; import javafx.scene.control.TextInputControl; import javafx.scene.image.ImageView; import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; import javafx.scene.layout.StackPane; -import javafx.scene.control.SplitPane; import javafx.stage.Stage; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; @@ -62,6 +62,13 @@ public class MainWindow extends UiPart { @FXML private ImageView placeholderImage; + /** + * Creates a new MainWindow with the given Stage and Logic. + * Initializes the window with default settings and accelerators. + * + * @param primaryStage The primary stage for this window + * @param logic The logic manager that handles command execution + */ public MainWindow(Stage primaryStage, Logic logic) { super(FXML, primaryStage); this.primaryStage = primaryStage; @@ -71,10 +78,18 @@ public MainWindow(Stage primaryStage, Logic logic) { helpWindow = new HelpWindow(); } + /** + * Returns the primary stage of the application. + * + * @return The primary stage + */ public Stage getPrimaryStage() { return primaryStage; } + /** + * Sets up keyboard accelerators for menu items. + */ private void setAccelerators() { setAccelerator(helpMenuItem, KeyCombination.valueOf("F1")); } @@ -146,6 +161,9 @@ public void handleHelp() { } } + /** + * Makes the main window visible. + */ void show() { primaryStage.show(); } @@ -163,7 +181,11 @@ private void handleExit() { } /** - * Handles the view command to display person details. + * Handles the view command by updating the UI to show person details. + * If the details panel is not visible, adds it to the split pane. + * Updates the person details based on the provided index in the command. + * + * @param commandText The full command text containing the view command and index */ private void handleViewCommand(String commandText) { if (!splitPane.getItems().contains(personDetailsPanelPlaceholder)) { @@ -217,5 +239,4 @@ private CommandResult executeCommand(String commandText) throws CommandException throw e; } } - -} \ No newline at end of file +} diff --git a/src/main/java/seedu/address/ui/PersonDetailPanel.java b/src/main/java/seedu/address/ui/PersonDetailPanel.java index 7ba03b49f81..bf145725a80 100644 --- a/src/main/java/seedu/address/ui/PersonDetailPanel.java +++ b/src/main/java/seedu/address/ui/PersonDetailPanel.java @@ -9,6 +9,11 @@ import seedu.address.commons.core.LogsCenter; import seedu.address.model.person.Person; +/** + * A UI component that displays detailed information about a {@code Person}. + * Shows all available fields including name, phone, address, email, job, income, tier, and remarks. + * Fields can be shown or hidden based on the presence of a person's data. + */ public class PersonDetailPanel extends UiPart { private static final String FXML = "PersonDetailPanel.fxml"; private final Logger logger = LogsCenter.getLogger(PersonDetailPanel.class); @@ -30,12 +35,20 @@ public class PersonDetailPanel extends UiPart { @FXML private Label remarkLabel; + /** + * Creates a new PersonDetailPanel. + * This constructor initializes all labels and hides them by default. + */ public PersonDetailPanel() { super(FXML); initializeLabels(); hideAllFields(); } + /** + * Initializes all FXML-injected labels, creating new ones if they are null. + * This ensures all label references are valid for manipulation. + */ private void initializeLabels() { nameLabel = nameLabel == null ? new Label() : nameLabel; phoneLabel = phoneLabel == null ? new Label() : phoneLabel; @@ -46,6 +59,10 @@ private void initializeLabels() { remarkLabel = remarkLabel == null ? new Label() : remarkLabel; } + /** + * Hides all fields in the detail panel by setting their managed and visible properties to false. + * This is used when no person is selected or when clearing the panel. + */ private void hideAllFields() { setManagedAndVisible(nameLabel, false); setManagedAndVisible(phoneLabel, false); @@ -57,6 +74,12 @@ private void hideAllFields() { setManagedAndVisible(remarkLabel, false); } + /** + * Sets both the managed and visible properties of a JavaFX node. + * + * @param node The JavaFX node to modify + * @param value The boolean value to set for both managed and visible properties + */ private void setManagedAndVisible(javafx.scene.Node node, boolean value) { if (node != null) { node.setManaged(value); @@ -64,6 +87,12 @@ private void setManagedAndVisible(javafx.scene.Node node, boolean value) { } } + /** + * Updates the detail panel to display information about the given person. + * If person is null, all fields will be hidden. + * + * @param person The person whose details should be displayed, can be null + */ public void setPersonDetails(Person person) { if (person != null) { showAllFields(); @@ -80,12 +109,24 @@ public void setPersonDetails(Person person) { } } + /** + * Sets the text of a label if the label is not null. + * + * @param label The label to update + * @param text The text to set + */ private void setLabelText(Label label, String text) { if (label != null) { label.setText(text); } } + /** + * Sets the tier display in the detail panel. + * Creates a new styled label for the tier and adds it to the tier pane. + * + * @param tier The tier value to display + */ private void setTier(String tier) { tierPane.getChildren().clear(); Label tierLabel = new Label(tier.toUpperCase()); @@ -93,6 +134,10 @@ private void setTier(String tier) { tierPane.getChildren().add(tierLabel); } + /** + * Shows all fields in the detail panel by setting their managed and visible properties to true. + * This is used when displaying a person's details. + */ private void showAllFields() { setManagedAndVisible(nameLabel, true); setManagedAndVisible(phoneLabel, true); @@ -103,4 +148,4 @@ private void showAllFields() { setManagedAndVisible(tierPane, true); setManagedAndVisible(remarkLabel, true); } -} \ No newline at end of file +} From b94e5d06c08c67170e347b67a8996802108f3142 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 05:16:15 +0800 Subject: [PATCH 23/45] Formatting test --- .../java/seedu/address/logic/commands/ViewCommandTest.java | 6 ++++-- .../seedu/address/logic/parser/ViewCommandParserTest.java | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/java/seedu/address/logic/commands/ViewCommandTest.java b/src/test/java/seedu/address/logic/commands/ViewCommandTest.java index c170cba0f48..4016872f43b 100644 --- a/src/test/java/seedu/address/logic/commands/ViewCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/ViewCommandTest.java @@ -1,6 +1,8 @@ package seedu.address.logic.commands; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static seedu.address.logic.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; import static seedu.address.testutil.Assert.assertThrows; import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; @@ -75,4 +77,4 @@ public void testCommandUsageMessage() { + "Parameters: INDEX (must be a positive integer)\n" + "Example: view 1", ViewCommand.MESSAGE_USAGE); } -} \ No newline at end of file +} diff --git a/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java index 9cb67240013..4a73641e031 100644 --- a/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java @@ -27,4 +27,4 @@ public void parse_invalidArgs_throwsParseException() { public void parse_emptyArg_throwsParseException() { assertParseFailure(parser, "", String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE)); } -} \ No newline at end of file +} From 75ada687e06be9cf84f4a48d10f692b0ff052aa9 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 05:23:15 +0800 Subject: [PATCH 24/45] Fix EOF errors --- src/main/resources/view/DarkTheme.css | 425 ------------------ src/main/resources/view/MainWindow.fxml | 70 --- .../resources/view/PersonDetailPanel.fxml | 55 --- 3 files changed, 550 deletions(-) delete mode 100644 src/main/resources/view/DarkTheme.css delete mode 100644 src/main/resources/view/MainWindow.fxml delete mode 100644 src/main/resources/view/PersonDetailPanel.fxml diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css deleted file mode 100644 index 72f58f46d78..00000000000 --- a/src/main/resources/view/DarkTheme.css +++ /dev/null @@ -1,425 +0,0 @@ -.background { - -fx-background-color: derive(#1d1d1d, 20%); - background-color: #383838; /* Used in the default.html file */ -} - -.label { - -fx-font-size: 11pt; - -fx-font-family: "Segoe UI Semibold"; - -fx-text-fill: #555555; - -fx-opacity: 0.9; -} - -.label-bright { - -fx-font-size: 11pt; - -fx-font-family: "Segoe UI Semibold"; - -fx-text-fill: white; - -fx-opacity: 1; -} - -.label-header { - -fx-font-size: 32pt; - -fx-font-family: "Segoe UI Light"; - -fx-text-fill: white; - -fx-opacity: 1; -} - -.text-field { - -fx-font-size: 12pt; - -fx-font-family: "Segoe UI Semibold"; -} - -.tab-pane { - -fx-padding: 0 0 0 1; -} - -.tab-pane .tab-header-area { - -fx-padding: 0 0 0 0; - -fx-min-height: 0; - -fx-max-height: 0; -} - -.table-view { - -fx-base: #1d1d1d; - -fx-control-inner-background: #1d1d1d; - -fx-background-color: #1d1d1d; - -fx-table-cell-border-color: transparent; - -fx-table-header-border-color: transparent; - -fx-padding: 5; -} - -.table-view .column-header-background { - -fx-background-color: transparent; -} - -.table-view .column-header, .table-view .filler { - -fx-size: 35; - -fx-border-width: 0 0 1 0; - -fx-background-color: transparent; - -fx-border-color: - transparent - transparent - derive(-fx-base, 80%) - transparent; - -fx-border-insets: 0 10 1 0; -} - -.table-view .column-header .label { - -fx-font-size: 20pt; - -fx-font-family: "Segoe UI Light"; - -fx-text-fill: white; - -fx-alignment: center-left; - -fx-opacity: 1; -} - -.table-view:focused .table-row-cell:filled:focused:selected { - -fx-background-color: -fx-focus-color; -} - -.split-pane:horizontal .split-pane-divider { - -fx-background-color: derive(#1d1d1d, 20%); - -fx-border-color: transparent transparent transparent #4d4d4d; -} - -.split-pane { - -fx-border-radius: 1; - -fx-border-width: 1; - -fx-background-color: derive(#1d1d1d, 20%); -} - -.list-view { - -fx-background-insets: 0; - -fx-padding: 0; - -fx-background-color: derive(#1d1d1d, 20%); -} - -.list-cell { - -fx-label-padding: 0 0 0 0; - -fx-graphic-text-gap : 0; - -fx-padding: 0 0 0 0; -} - -.list-cell:filled:even { - -fx-background-color: #3c3e3f; -} - -.list-cell:filled:odd { - -fx-background-color: #515658; -} - -.list-cell:filled:selected { - -fx-background-color: #424d5f; -} - -.list-cell:filled:selected #cardPane { - -fx-border-color: #3e7b91; - -fx-border-width: 1; -} - -.list-cell .label { - -fx-text-fill: white; -} - -.cell_big_label { - -fx-font-family: "Segoe UI Semibold"; - -fx-font-size: 16px; - -fx-text-fill: #010504; -} - -.cell_small_label { - -fx-font-family: "Segoe UI"; - -fx-font-size: 13px; - -fx-text-fill: #010504; -} - -.stack-pane { - -fx-background-color: derive(#1d1d1d, 20%); -} - -.pane-with-border { - -fx-background-color: derive(#1d1d1d, 20%); - -fx-border-color: derive(#1d1d1d, 10%); - -fx-border-top-width: 1px; -} - -.status-bar { - -fx-background-color: derive(#1d1d1d, 30%); -} - -.result-display { - -fx-background-color: transparent; - -fx-font-family: "Segoe UI Light"; - -fx-font-size: 13pt; - -fx-text-fill: white; -} - -.result-display .label { - -fx-text-fill: black !important; -} - -.status-bar .label { - -fx-font-family: "Segoe UI Light"; - -fx-text-fill: white; - -fx-padding: 4px; - -fx-pref-height: 30px; -} - -.status-bar-with-border { - -fx-background-color: derive(#1d1d1d, 30%); - -fx-border-color: derive(#1d1d1d, 25%); - -fx-border-width: 1px; -} - -.status-bar-with-border .label { - -fx-text-fill: white; -} - -.grid-pane { - -fx-background-color: derive(#1d1d1d, 30%); - -fx-border-color: derive(#1d1d1d, 30%); - -fx-border-width: 1px; -} - -.grid-pane .stack-pane { - -fx-background-color: derive(#1d1d1d, 30%); -} - -.context-menu { - -fx-background-color: derive(#1d1d1d, 50%); -} - -.context-menu .label { - -fx-text-fill: white; -} - -.menu-bar { - -fx-background-color: derive(#1d1d1d, 20%); -} - -.menu-bar .label { - -fx-font-size: 14pt; - -fx-font-family: "Segoe UI Light"; - -fx-text-fill: white; - -fx-opacity: 0.9; -} - -.menu .left-container { - -fx-background-color: black; -} - -/* - * Metro style Push Button - * Author: Pedro Duque Vieira - * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/ - */ -.button { - -fx-padding: 5 22 5 22; - -fx-border-color: #e2e2e2; - -fx-border-width: 2; - -fx-background-radius: 0; - -fx-background-color: #1d1d1d; - -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif; - -fx-font-size: 11pt; - -fx-text-fill: #d8d8d8; - -fx-background-insets: 0 0 0 0, 0, 1, 2; -} - -.button:hover { - -fx-background-color: #3a3a3a; -} - -.button:pressed, .button:default:hover:pressed { - -fx-background-color: white; - -fx-text-fill: #1d1d1d; -} - -.button:focused { - -fx-border-color: white, white; - -fx-border-width: 1, 1; - -fx-border-style: solid, segments(1, 1); - -fx-border-radius: 0, 0; - -fx-border-insets: 1 1 1 1, 0; -} - -.button:disabled, .button:default:disabled { - -fx-opacity: 0.4; - -fx-background-color: #1d1d1d; - -fx-text-fill: white; -} - -.button:default { - -fx-background-color: -fx-focus-color; - -fx-text-fill: #ffffff; -} - -.button:default:hover { - -fx-background-color: derive(-fx-focus-color, 30%); -} - -.dialog-pane { - -fx-background-color: #1d1d1d; -} - -.dialog-pane > *.button-bar > *.container { - -fx-background-color: #1d1d1d; -} - -.dialog-pane > *.label.content { - -fx-font-size: 14px; - -fx-font-weight: bold; - -fx-text-fill: white; -} - -.dialog-pane:header *.header-panel { - -fx-background-color: derive(#1d1d1d, 25%); -} - -.dialog-pane:header *.header-panel *.label { - -fx-font-size: 18px; - -fx-font-style: italic; - -fx-fill: white; - -fx-text-fill: white; -} - -.scroll-bar { - -fx-background-color: derive(#1d1d1d, 20%); -} - -.scroll-bar .thumb { - -fx-background-color: derive(#1d1d1d, 50%); - -fx-background-insets: 3; -} - -.scroll-bar .increment-button, .scroll-bar .decrement-button { - -fx-background-color: transparent; - -fx-padding: 0 0 0 0; -} - -.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow { - -fx-shape: " "; -} - -.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow { - -fx-padding: 1 8 1 8; -} - -.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow { - -fx-padding: 8 1 8 1; -} - -.cell_small_label { - -fx-font-family: "Segoe UI"; - -fx-font-size: 13px; - -fx-text-fill: #a0a0a0; -} - -.cell_big_label { - -fx-font-family: "Segoe UI Bold"; - -fx-font-size: 16px; - -fx-text-fill: white; -} - -.name-label { - -fx-font-weight: bold; -} - -.detail-tier-label { - -fx-text-fill: white; - -fx-padding: 2 5 2 5; - -fx-border-radius: 3; - -fx-background-radius: 3; - -fx-font-size: 12px; -} - -/* Tier-specific styles for the detail panel */ -.detail-tier-label.gold-tier { - -fx-background-color: #B59410; -} - -.detail-tier-label.silver-tier { - -fx-background-color: #71706e; -} - -.detail-tier-label.bronze-tier { - -fx-background-color: #804a00; -} - -.detail-tier-label.reject-tier { - -fx-background-color: #8B0000; -} - -#cardPane { - -fx-background-color: transparent; - -fx-border-width: 0; -} - -#commandTypeLabel { - -fx-font-size: 11px; - -fx-text-fill: #F70D1A; -} - -#commandTextArea { - -fx-background-color: transparent #383838 transparent #383838; - -fx-background-insets: 0; - -fx-border-color: #383838 #383838 #ffffff #383838; - -fx-border-insets: 0; - -fx-border-width: 1; - -fx-control-inner-background: #383838; - -fx-font-family: "Segoe UI Light"; - -fx-font-size: 13pt; - -fx-text-fill: white; - -fx-pref-height: 40px; - -fx-prompt-text-fill: grey; - -fx-min-height: 40px; - -fx-max-height: 200px; -} - -#filterField, #personListPanel, #personWebpage { - -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); -} - -#resultDisplay .content { - -fx-background-color: transparent, #383838, transparent, #383838; - -fx-background-radius: 0; -} - -#assignedTier { - -fx-hgap: 7; - -fx-vgap: 3; -} - -#assignedTier .gold-tier { - -fx-background-color: #B59410; -} - -#assignedTier .silver-tier { - -fx-background-color: #71706e; -} - -#assignedTier .bronze-tier { - -fx-background-color: #804a00; -} - -#assignedTier .reject-tier { - -fx-background-color: #8B0000; -} - -#assignedTier .label { - -fx-text-fill: white; - -fx-padding: 1 3 1 3; - -fx-border-radius: 2; - -fx-background-radius: 2; - -fx-font-size: 11; -} - -#personDetailsPanelPlaceholder { - -fx-background-color: #383838; -} - -.scroll-pane { - -fx-background-color: #383838; -} - -.scroll-pane > .viewport { - -fx-background-color: #383838; -} \ No newline at end of file diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml deleted file mode 100644 index 657e86a373d..00000000000 --- a/src/main/resources/view/MainWindow.fxml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/view/PersonDetailPanel.fxml b/src/main/resources/view/PersonDetailPanel.fxml deleted file mode 100644 index 956f2cecbb4..00000000000 --- a/src/main/resources/view/PersonDetailPanel.fxml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 3437adf32fb84f942a212e8b2b42030d19a41059 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 06:01:02 +0800 Subject: [PATCH 25/45] Fix EOF 2 --- src/main/resources/view/DarkTheme.css | 425 ++++++++++++++++++ src/main/resources/view/MainWindow.fxml | 70 +++ .../resources/view/PersonDetailPanel.fxml | 55 +++ 3 files changed, 550 insertions(+) create mode 100644 src/main/resources/view/DarkTheme.css create mode 100644 src/main/resources/view/MainWindow.fxml create mode 100644 src/main/resources/view/PersonDetailPanel.fxml diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css new file mode 100644 index 00000000000..e7d9768c499 --- /dev/null +++ b/src/main/resources/view/DarkTheme.css @@ -0,0 +1,425 @@ +.background { + -fx-background-color: derive(#1d1d1d, 20%); + background-color: #383838; /* Used in the default.html file */ +} + +.label { + -fx-font-size: 11pt; + -fx-font-family: "Segoe UI Semibold"; + -fx-text-fill: #555555; + -fx-opacity: 0.9; +} + +.label-bright { + -fx-font-size: 11pt; + -fx-font-family: "Segoe UI Semibold"; + -fx-text-fill: white; + -fx-opacity: 1; +} + +.label-header { + -fx-font-size: 32pt; + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: white; + -fx-opacity: 1; +} + +.text-field { + -fx-font-size: 12pt; + -fx-font-family: "Segoe UI Semibold"; +} + +.tab-pane { + -fx-padding: 0 0 0 1; +} + +.tab-pane .tab-header-area { + -fx-padding: 0 0 0 0; + -fx-min-height: 0; + -fx-max-height: 0; +} + +.table-view { + -fx-base: #1d1d1d; + -fx-control-inner-background: #1d1d1d; + -fx-background-color: #1d1d1d; + -fx-table-cell-border-color: transparent; + -fx-table-header-border-color: transparent; + -fx-padding: 5; +} + +.table-view .column-header-background { + -fx-background-color: transparent; +} + +.table-view .column-header, .table-view .filler { + -fx-size: 35; + -fx-border-width: 0 0 1 0; + -fx-background-color: transparent; + -fx-border-color: + transparent + transparent + derive(-fx-base, 80%) + transparent; + -fx-border-insets: 0 10 1 0; +} + +.table-view .column-header .label { + -fx-font-size: 20pt; + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: white; + -fx-alignment: center-left; + -fx-opacity: 1; +} + +.table-view:focused .table-row-cell:filled:focused:selected { + -fx-background-color: -fx-focus-color; +} + +.split-pane:horizontal .split-pane-divider { + -fx-background-color: derive(#1d1d1d, 20%); + -fx-border-color: transparent transparent transparent #4d4d4d; +} + +.split-pane { + -fx-border-radius: 1; + -fx-border-width: 1; + -fx-background-color: derive(#1d1d1d, 20%); +} + +.list-view { + -fx-background-insets: 0; + -fx-padding: 0; + -fx-background-color: derive(#1d1d1d, 20%); +} + +.list-cell { + -fx-label-padding: 0 0 0 0; + -fx-graphic-text-gap : 0; + -fx-padding: 0 0 0 0; +} + +.list-cell:filled:even { + -fx-background-color: #3c3e3f; +} + +.list-cell:filled:odd { + -fx-background-color: #515658; +} + +.list-cell:filled:selected { + -fx-background-color: #424d5f; +} + +.list-cell:filled:selected #cardPane { + -fx-border-color: #3e7b91; + -fx-border-width: 1; +} + +.list-cell .label { + -fx-text-fill: white; +} + +.cell_big_label { + -fx-font-family: "Segoe UI Semibold"; + -fx-font-size: 16px; + -fx-text-fill: #010504; +} + +.cell_small_label { + -fx-font-family: "Segoe UI"; + -fx-font-size: 13px; + -fx-text-fill: #010504; +} + +.stack-pane { + -fx-background-color: derive(#1d1d1d, 20%); +} + +.pane-with-border { + -fx-background-color: derive(#1d1d1d, 20%); + -fx-border-color: derive(#1d1d1d, 10%); + -fx-border-top-width: 1px; +} + +.status-bar { + -fx-background-color: derive(#1d1d1d, 30%); +} + +.result-display { + -fx-background-color: transparent; + -fx-font-family: "Segoe UI Light"; + -fx-font-size: 13pt; + -fx-text-fill: white; +} + +.result-display .label { + -fx-text-fill: black !important; +} + +.status-bar .label { + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: white; + -fx-padding: 4px; + -fx-pref-height: 30px; +} + +.status-bar-with-border { + -fx-background-color: derive(#1d1d1d, 30%); + -fx-border-color: derive(#1d1d1d, 25%); + -fx-border-width: 1px; +} + +.status-bar-with-border .label { + -fx-text-fill: white; +} + +.grid-pane { + -fx-background-color: derive(#1d1d1d, 30%); + -fx-border-color: derive(#1d1d1d, 30%); + -fx-border-width: 1px; +} + +.grid-pane .stack-pane { + -fx-background-color: derive(#1d1d1d, 30%); +} + +.context-menu { + -fx-background-color: derive(#1d1d1d, 50%); +} + +.context-menu .label { + -fx-text-fill: white; +} + +.menu-bar { + -fx-background-color: derive(#1d1d1d, 20%); +} + +.menu-bar .label { + -fx-font-size: 14pt; + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: white; + -fx-opacity: 0.9; +} + +.menu .left-container { + -fx-background-color: black; +} + +/* + * Metro style Push Button + * Author: Pedro Duque Vieira + * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/ + */ +.button { + -fx-padding: 5 22 5 22; + -fx-border-color: #e2e2e2; + -fx-border-width: 2; + -fx-background-radius: 0; + -fx-background-color: #1d1d1d; + -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif; + -fx-font-size: 11pt; + -fx-text-fill: #d8d8d8; + -fx-background-insets: 0 0 0 0, 0, 1, 2; +} + +.button:hover { + -fx-background-color: #3a3a3a; +} + +.button:pressed, .button:default:hover:pressed { + -fx-background-color: white; + -fx-text-fill: #1d1d1d; +} + +.button:focused { + -fx-border-color: white, white; + -fx-border-width: 1, 1; + -fx-border-style: solid, segments(1, 1); + -fx-border-radius: 0, 0; + -fx-border-insets: 1 1 1 1, 0; +} + +.button:disabled, .button:default:disabled { + -fx-opacity: 0.4; + -fx-background-color: #1d1d1d; + -fx-text-fill: white; +} + +.button:default { + -fx-background-color: -fx-focus-color; + -fx-text-fill: #ffffff; +} + +.button:default:hover { + -fx-background-color: derive(-fx-focus-color, 30%); +} + +.dialog-pane { + -fx-background-color: #1d1d1d; +} + +.dialog-pane > *.button-bar > *.container { + -fx-background-color: #1d1d1d; +} + +.dialog-pane > *.label.content { + -fx-font-size: 14px; + -fx-font-weight: bold; + -fx-text-fill: white; +} + +.dialog-pane:header *.header-panel { + -fx-background-color: derive(#1d1d1d, 25%); +} + +.dialog-pane:header *.header-panel *.label { + -fx-font-size: 18px; + -fx-font-style: italic; + -fx-fill: white; + -fx-text-fill: white; +} + +.scroll-bar { + -fx-background-color: derive(#1d1d1d, 20%); +} + +.scroll-bar .thumb { + -fx-background-color: derive(#1d1d1d, 50%); + -fx-background-insets: 3; +} + +.scroll-bar .increment-button, .scroll-bar .decrement-button { + -fx-background-color: transparent; + -fx-padding: 0 0 0 0; +} + +.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow { + -fx-shape: " "; +} + +.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow { + -fx-padding: 1 8 1 8; +} + +.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow { + -fx-padding: 8 1 8 1; +} + +.cell_small_label { + -fx-font-family: "Segoe UI"; + -fx-font-size: 13px; + -fx-text-fill: #a0a0a0; +} + +.cell_big_label { + -fx-font-family: "Segoe UI Bold"; + -fx-font-size: 16px; + -fx-text-fill: white; +} + +.name-label { + -fx-font-weight: bold; +} + +.detail-tier-label { + -fx-text-fill: white; + -fx-padding: 2 5 2 5; + -fx-border-radius: 3; + -fx-background-radius: 3; + -fx-font-size: 12px; +} + +/* Tier-specific styles for the detail panel */ +.detail-tier-label.gold-tier { + -fx-background-color: #B59410; +} + +.detail-tier-label.silver-tier { + -fx-background-color: #71706e; +} + +.detail-tier-label.bronze-tier { + -fx-background-color: #804a00; +} + +.detail-tier-label.reject-tier { + -fx-background-color: #8B0000; +} + +#cardPane { + -fx-background-color: transparent; + -fx-border-width: 0; +} + +#commandTypeLabel { + -fx-font-size: 11px; + -fx-text-fill: #F70D1A; +} + +#commandTextArea { + -fx-background-color: transparent #383838 transparent #383838; + -fx-background-insets: 0; + -fx-border-color: #383838 #383838 #ffffff #383838; + -fx-border-insets: 0; + -fx-border-width: 1; + -fx-control-inner-background: #383838; + -fx-font-family: "Segoe UI Light"; + -fx-font-size: 13pt; + -fx-text-fill: white; + -fx-pref-height: 40px; + -fx-prompt-text-fill: grey; + -fx-min-height: 40px; + -fx-max-height: 200px; +} + +#filterField, #personListPanel, #personWebpage { + -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); +} + +#resultDisplay .content { + -fx-background-color: transparent, #383838, transparent, #383838; + -fx-background-radius: 0; +} + +#assignedTier { + -fx-hgap: 7; + -fx-vgap: 3; +} + +#assignedTier .gold-tier { + -fx-background-color: #B59410; +} + +#assignedTier .silver-tier { + -fx-background-color: #71706e; +} + +#assignedTier .bronze-tier { + -fx-background-color: #804a00; +} + +#assignedTier .reject-tier { + -fx-background-color: #8B0000; +} + +#assignedTier .label { + -fx-text-fill: white; + -fx-padding: 1 3 1 3; + -fx-border-radius: 2; + -fx-background-radius: 2; + -fx-font-size: 11; +} + +#personDetailsPanelPlaceholder { + -fx-background-color: #383838; +} + +.scroll-pane { + -fx-background-color: #383838; +} + +.scroll-pane > .viewport { + -fx-background-color: #383838; +} diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 00000000000..7760fa3c805 --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/PersonDetailPanel.fxml b/src/main/resources/view/PersonDetailPanel.fxml new file mode 100644 index 00000000000..50e2c59e196 --- /dev/null +++ b/src/main/resources/view/PersonDetailPanel.fxml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + From f674e9a44bd8b18b7e15ca460c8f0ffe8cd48764 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 06:15:32 +0800 Subject: [PATCH 26/45] Add close command --- .../address/logic/commands/CloseCommand.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/java/seedu/address/logic/commands/CloseCommand.java diff --git a/src/main/java/seedu/address/logic/commands/CloseCommand.java b/src/main/java/seedu/address/logic/commands/CloseCommand.java new file mode 100644 index 00000000000..25eea63463a --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/CloseCommand.java @@ -0,0 +1,28 @@ +package seedu.address.logic.commands; + +import seedu.address.model.Model; + +/** + * Closes the detail view in the address book. + */ +public class CloseCommand extends Command { + + public static final String COMMAND_WORD = "close"; + + public static final String MESSAGE_SUCCESS = "Detail view closed."; + + @Override + public CommandResult execute(Model model) { + return new CommandResult(MESSAGE_SUCCESS); + } + + @Override + public boolean equals(Object obj) { + return obj == this || obj instanceof CloseCommand; + } + + @Override + public String toString() { + return COMMAND_WORD; + } +} From 26c58ee9379e5792c5e345138cb1045c1f9aec46 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 06:15:39 +0800 Subject: [PATCH 27/45] Update parser --- .../java/seedu/address/logic/parser/AddressBookParser.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index 9289d33708e..1159c88d912 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -10,6 +10,7 @@ import seedu.address.commons.core.LogsCenter; import seedu.address.logic.commands.AddCommand; import seedu.address.logic.commands.ClearCommand; +import seedu.address.logic.commands.CloseCommand; import seedu.address.logic.commands.Command; import seedu.address.logic.commands.DeleteCommand; import seedu.address.logic.commands.EditCommand; @@ -82,6 +83,9 @@ public Command parseCommand(String userInput) throws ParseException { case ViewCommand.COMMAND_WORD: return new ViewCommandParser().parse(arguments); + case CloseCommand.COMMAND_WORD: + return new CloseCommand(); + default: logger.finer("This user input caused a ParseException: " + userInput); throw new ParseException(MESSAGE_UNKNOWN_COMMAND); From 9623c3474c9d88b749bc3f6a8aebfafb370dacd8 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 06:15:43 +0800 Subject: [PATCH 28/45] Update UI --- src/main/java/seedu/address/ui/MainWindow.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index a1d8773c84c..4d25ab1b299 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -209,6 +209,10 @@ private void handleViewCommand(String commandText) { } } + private void handleCloseCommand() { + splitPane.getItems().remove(personDetailsPanelPlaceholder); + } + /** * Executes the command and returns the result. * @@ -222,6 +226,8 @@ private CommandResult executeCommand(String commandText) throws CommandException if (commandText.trim().toLowerCase().startsWith("view")) { handleViewCommand(commandText); + } else if (commandText.trim().toLowerCase().equals("close")) { + handleCloseCommand(); } if (commandResult.isShowHelp()) { From b5db4255f7f184de1336f602d32a24e189f2880f Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 06:15:50 +0800 Subject: [PATCH 29/45] Add test for close command --- .../logic/commands/CloseCommandTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/test/java/seedu/address/logic/commands/CloseCommandTest.java diff --git a/src/test/java/seedu/address/logic/commands/CloseCommandTest.java b/src/test/java/seedu/address/logic/commands/CloseCommandTest.java new file mode 100644 index 00000000000..378d7c35afd --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/CloseCommandTest.java @@ -0,0 +1,47 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; + +public class CloseCommandTest { + + private Model model = new ModelManager(); + @Test + public void execute_close_success() { + CloseCommand closeCommand = new CloseCommand(); + CommandResult commandResult = closeCommand.execute(model); + + assertEquals(CloseCommand.MESSAGE_SUCCESS, commandResult.getFeedbackToUser()); + assertFalse(commandResult.isShowHelp()); + assertFalse(commandResult.isExit()); + } + + @Test + public void equals() { + CloseCommand closeCommand = new CloseCommand(); + + // same object -> returns true + assertTrue(closeCommand.equals(closeCommand)); + + // same type -> returns true + assertTrue(closeCommand.equals(new CloseCommand())); + + // null -> returns false + assertFalse(closeCommand.equals(null)); + + // different types -> returns false + assertFalse(closeCommand.equals(1)); + } + + @Test + public void toStringMethod() { + CloseCommand closeCommand = new CloseCommand(); + assertEquals(CloseCommand.COMMAND_WORD, closeCommand.toString()); + } +} From cd4f99408617b011a5714fd93cbd2736d6c70299 Mon Sep 17 00:00:00 2001 From: leyew <102467346+itsme-zeix@users.noreply.github.com> Date: Tue, 22 Oct 2024 12:58:03 +0800 Subject: [PATCH 30/45] Add suggested changes --- docs/UserGuide.md | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 80be6374415..2446aecca90 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -5,7 +5,7 @@ title: User Guide ![Banner](images/AgentAssistBanner.png) # Welcome to the AgentAssist User Guide! -The **AgentAssist User Guide** is here to help you unlock the full potential of **AgentAssist** and take your credit card sales to the next level. his guide offers clear, step-by-step instructions and practical examples to help you get the most out of the application. +The **AgentAssist User Guide** is here to help you unlock the full potential of **AgentAssist** and take your credit card sales to the next level. This guide offers clear, step-by-step instructions and practical examples to help you get the most out of the application. In this guide, you'll learn how to: * **Set Up AgentAssist** @@ -67,7 +67,7 @@ AgentAssist is the **definitive desktop tool for credit card sales agents**. Mer **Overview of Key Features:** * **Contact Management**: * Manage your client details easily. Add, edit, and delete contacts to keep all your client information in one accessible place. -* **Keyboard-only Navigation**: +* **Keyboard-centric Navigation**: * Navigate through the application entirely via keyboard shortcuts, improving workflow efficiency. * **Multi-Level Filtering**: * Filter your data by multiple criteria to find exactly who you’re looking for. @@ -87,7 +87,6 @@ Before you start using AgentAssist, there are a few prerequisites to ensure you ### Familiarity with Keyboard Navigation AgentAssist is designed to enhance speed and efficiency, with a strong focus on **keyboard-based navigation**. While the application includes a Graphical User Interface (GUI), its full potential is unlocked when you use **keyboard commands**. Therefore, it is important to: -- Be comfortable navigating applications using the keyboard. - Familiarize yourself with basic `Command Line Interface (CLI)` commands if you haven't already. This will make it easier to use AgentAssist’s command system effectively. - Know common keyboard shortcuts (e.g., `Enter`, `Arrow keys`, etc.). @@ -176,11 +175,11 @@ Each command in AgentAssist consists of three key components: the **command**, * Let's take a look at the structure in more detail: -| **Components** | **Description** | **Example** | -|:----------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------| -| **Command** | The action you want AgentAssist to perform. | `add` | -| **Flag(s)** | Modifiers that specify what kind of data is being handled.

Flag(s) are typically 1-2 letters followed by a backslash. | `n/`, `p/`, `r/`, `rn/` | -| **Argument(s)** | The values or inputs the command uses, such as client data or specific details.

The user guide may represent it as a placeholder using ``. | `John Doe`, `john@example.com` | +| **Components** | **Description** | **Example** | +|:----------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------| +| **Command** | The action you want AgentAssist to perform. | `add` | +| **Flag(s)** | Modifiers that specify what kind of data is being handled.

Flag(s) are typically 1-2 letters followed by a backslash. | `n/`, `p/`, `r/`, `rn/` | +| **Argument(s)** | The values or inputs the command uses, such as client data or specific details.

This guide may represent it as a placeholder using ``. | `John Doe`, `john@example.com` | Here's an example that uses multiple flags and arguments: ``` @@ -241,16 +240,16 @@ Refer to the table below for more details. | **Flag** | **Expected Argument** | **Description** | **Requirements** | **Case Sensitivity** | |----------|-----------------------|------------------------------------------------|-------------------------------------------------------------------------------------------------|-----------------------| -| `n/` | `` | The client's full name | Letters, numbers, and spaces only | ❌ | +| `n/` | `` | The client's full name | Any combination of letters, numbers, and spaces (no symbols). | ❌ | | `p/` | `` | The client's phone number | Valid Singapore phone number:
• 8-digit number
• Starts with 8 or 9 | ❌ | | `e/` | `` | The client's email address | Valid email format (`username@domain.com`) | ❌ | -| `a/` | `
` | The client's physical address | No specific restrictions | ❌ | -| `j/` | `` | The client's job title or profession | No specific restrictions | ❌ | +| `a/` | `
` | The client's physical address | Any combination of letters, numbers, spaces, and symbols. | ❌ | +| `j/` | `` | The client's job title or profession | Any combination of letters, numbers, spaces, and symbols. | ❌ | | `i/` | `` | The client's annual income | Positive number or zero
• Cannot include commas and decimal points
• Must be numeric | ❌ | -| `t/` | `` | The client's assigned tier level | Predefined tiers:
• Gold, Silver, Bronze, Reject | ✔️ | -| `r/` | `` | General remarks about the client | No specific restrictions | ❌ | -| `ra/` | `` | Append information to the existing remark | No specific restrictions | ❌ | -| `rn/` | `` | Replaces the existing remark with a new remark | No specific restrictions | ❌ | +| `t/` | `` | The client's assigned tier level | Must be one of the predefined tiers:
• Gold, Silver, Bronze, Reject | ✔️ | +| `r/` | `` | General remarks about the client | Any combination of letters, numbers, spaces, and symbols. | ❌ | +| `ra/` | `` | Append information to the existing remark | Any combination of letters, numbers, spaces, and symbols. | ❌ | +| `rn/` | `` | Replaces the existing remark with a new remark | Any combination of letters, numbers, spaces, and symbols. | ❌ | > 💡 **Pro Tip:** > @@ -276,7 +275,7 @@ Some initial commands to try: * `delete 3`: Removes the third client from your list. Ensure you have the correct index to avoid deleting the wrong client. **Searching for a Client** -* `filter n/Jane`: Finds all client named Jane in your database. It’s a powerful tool for quickly locating clients or filtering for a specific type of client. +* `filter n/Jane`: Finds all clients named Jane in your database. It’s a powerful tool for quickly locating clients or filtering for a specific type of client. **Getting Help** * `help`: Opens a help dialog that provides a summary of all available commands and their usage. @@ -359,7 +358,7 @@ For detailed explanations of each flag and acceptable arguments, refer to Sectio > **Note on Duplicates:** > -> AgentAssist will prevent duplicate entries if a client with the **same name, email, job, and income** is already saved. +> AgentAssist will prevent duplicate entries if a client with the **same name, email and phone number** is already saved. > When this happens, you will see the following message: > > ``` @@ -704,7 +703,7 @@ Each credit card tier is visually distinguished in the UI: Gold is marked with a | **Delete Existing Client** | `delete ` | `delete 69` | | **Edit Existing Client** | `edit n/ p/ e/ a/
j/ i/ [t/] [rn/] [ra/]` | `edit 69 n/ GORDON MOORE p/ 77337733 e/ gmoore_new@ntu.sg a/ COM3 j/ doctor i/ 1000000000 ra/ added info` | | **List All Clients** | `list` | `list` | -| **Filter Client List** | `filter n/ p/ e/ a/
j/ r/ t/ i/` | `filter n/ GORDON MOORE j/ doctor t/ gold` | +| **Filter Client List** | `filter [n/] [p/] [e/] [a/
] [j/] [r/] [t/] [i/]` | `filter n/ GORDON MOORE j/ doctor t/ gold` | | **View Client Details** | `view` | `view` | | **Close Client Details** | `close` | `close` | | **View Help** | `help` | `help` | From d3f084ac96fd08516d01edf49dde33030ba317fc Mon Sep 17 00:00:00 2001 From: Colin Hia Date: Tue, 22 Oct 2024 22:40:01 +0800 Subject: [PATCH 31/45] PersonCardField class: Update field to use Icons Field labels are displayed in bolded text. Users are unable to quickly identify fields at one glance. Let's use Icons to allow users to quickly identify fields. Using Icons is preferable over images as it allows for Icons to be used in other UI elements. --- build.gradle | 4 +++ .../java/seedu/address/ui/PersonCard.java | 10 +++---- .../seedu/address/ui/PersonCardField.java | 29 +++++++++++++------ 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/build.gradle b/build.gradle index 198cd695c01..a94123f3e76 100644 --- a/build.gradle +++ b/build.gradle @@ -64,6 +64,10 @@ dependencies { implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.7.0' implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.7.4' + implementation 'org.kordamp.ikonli:ikonli-core:12.3.0' + implementation group: 'org.kordamp.ikonli', name: 'ikonli-fontawesome5-pack', version: '12.3.1' + implementation 'org.kordamp.ikonli:ikonli-javafx:12.3.1' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: jUnitVersion testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: jUnitVersion } diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java index 2a0fd281d7e..ff5320586c2 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/seedu/address/ui/PersonCard.java @@ -54,11 +54,11 @@ public PersonCard(Person person, int displayedIndex) { } private void createFields() { name.setText(person.getName().fullName); - phone.setText("Phone:", person.getPhone().value); - address.setText("Address:", person.getAddress().value); - email.setText("Email:", person.getEmail().value); - job.setText("Job:", person.getJob().value); - income.setText("Income:", person.getIncome().toString()); + phone.setFields("fas-phone-alt", person.getPhone().value); + address.setFields("fas-building", person.getAddress().value); + email.setFields("fas-envelope", person.getEmail().value); + job.setFields("fas-briefcase", person.getJob().value); + income.setFields("fas-dollar-sign", person.getIncome().toString()); remark.setText(person.getRemark().value); cardFields.getChildren().addAll(phone, address, email, job, income); } diff --git a/src/main/java/seedu/address/ui/PersonCardField.java b/src/main/java/seedu/address/ui/PersonCardField.java index 606eb671539..957eb8e793e 100644 --- a/src/main/java/seedu/address/ui/PersonCardField.java +++ b/src/main/java/seedu/address/ui/PersonCardField.java @@ -1,14 +1,18 @@ package seedu.address.ui; +import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import javafx.scene.paint.Paint; +import org.kordamp.ikonli.javafx.FontIcon; + /** * Represents a labelled field in the PersonCard */ public class PersonCardField extends HBox { - private Label field; + private FontIcon icon; private Label value; /** @@ -16,21 +20,28 @@ public class PersonCardField extends HBox { */ public PersonCardField() { super(5); - field = new Label(); - field.setStyle("-fx-font-weight: bold;"); + icon = new FontIcon(); + icon.setIconColor(Paint.valueOf("darkgray")); + + HBox iconWrapper = new HBox(); + iconWrapper.setPrefWidth(20); + iconWrapper.setAlignment(Pos.CENTER); + iconWrapper.getChildren().add(icon); + value = new Label(); HBox.setHgrow(value, Priority.ALWAYS); value.setWrapText(true); - getChildren().addAll(field, value); + getChildren().addAll(iconWrapper, value); } /** - * Updates the {@code Label} {@code field} and {@code value} with the corresponding strings. - * @param fieldText The String to be updated into field. - * @param valueText The String to be updated in value. + * Updates the {@code PersonCardField} {@code Icon} and {@code Value} with the corresponding strings. + * + * @param iconLiteral The IconLiteral to be set for the icon. + * @param valueText The String to be updated in value. */ - public void setText(String fieldText, String valueText) { - this.field.setText(fieldText); + public void setFields(String iconLiteral, String valueText) { + this.icon.setIconLiteral(iconLiteral); this.value.setText(valueText); } } From 4c7ef5ef31bf900ebccec2ed78cbe797876a1704 Mon Sep 17 00:00:00 2001 From: Colin Hia Date: Tue, 22 Oct 2024 22:49:13 +0800 Subject: [PATCH 32/45] Update Coding Style for Checkstyle --- src/main/java/seedu/address/ui/PersonCardField.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/address/ui/PersonCardField.java b/src/main/java/seedu/address/ui/PersonCardField.java index 957eb8e793e..00c75ce829a 100644 --- a/src/main/java/seedu/address/ui/PersonCardField.java +++ b/src/main/java/seedu/address/ui/PersonCardField.java @@ -4,8 +4,8 @@ import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; - import javafx.scene.paint.Paint; + import org.kordamp.ikonli.javafx.FontIcon; /** From 6d7f47c9912be5f4816e55d6479172157c5e06e2 Mon Sep 17 00:00:00 2001 From: Colin Hia Date: Tue, 22 Oct 2024 22:54:28 +0800 Subject: [PATCH 33/45] Update Coding Style for Checkstyle --- src/main/java/seedu/address/ui/PersonCardField.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/address/ui/PersonCardField.java b/src/main/java/seedu/address/ui/PersonCardField.java index 00c75ce829a..2763136ba6c 100644 --- a/src/main/java/seedu/address/ui/PersonCardField.java +++ b/src/main/java/seedu/address/ui/PersonCardField.java @@ -1,13 +1,13 @@ package seedu.address.ui; +import org.kordamp.ikonli.javafx.FontIcon; + import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.paint.Paint; -import org.kordamp.ikonli.javafx.FontIcon; - /** * Represents a labelled field in the PersonCard */ From 63e59c85b7623169694dddcc7105ce0bfc20f0e9 Mon Sep 17 00:00:00 2001 From: Pleng Naps <64528094+iamdiluxedbutcooler@users.noreply.github.com> Date: Wed, 23 Oct 2024 04:37:08 +0800 Subject: [PATCH 34/45] Update src/main/java/seedu/address/model/ModelManager.java Co-authored-by: leyew <102467346+itsme-zeix@users.noreply.github.com> --- src/main/java/seedu/address/model/ModelManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 2b2a7543fa1..4dc586802de 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -143,7 +143,7 @@ public void setSelectedPerson(Person person) { */ private void ensureSelectedPersonIsValid(ListChangeListener.Change change) { while (change.next()) { - if (selectedPerson.getValue() == null) { + if (this.getSelectedPerson() == null) { return; } From 261223e13961f681fcd26038cdf376ec0e86fc55 Mon Sep 17 00:00:00 2001 From: Pleng Naps <64528094+iamdiluxedbutcooler@users.noreply.github.com> Date: Wed, 23 Oct 2024 04:37:25 +0800 Subject: [PATCH 35/45] Update src/main/java/seedu/address/model/ModelManager.java Co-authored-by: leyew <102467346+itsme-zeix@users.noreply.github.com> --- src/main/java/seedu/address/model/ModelManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 4dc586802de..1e390bbeab5 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -150,7 +150,7 @@ private void ensureSelectedPersonIsValid(ListChangeListener.Change selectedPerson.getValue().isSamePerson(removedPerson)); if (wasSelectedPersonRemoved) { - selectedPerson.setValue(change.getFrom() > 0 ? filteredPersons.get(change.getFrom() - 1) : null); + this.setSelectedPerson(change.getFrom() > 0 ? filteredPersons.get(change.getFrom() - 1) : null); } } } From 658be23d079e541e917b8175fb1e7db2faca84d9 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Wed, 23 Oct 2024 04:36:44 +0800 Subject: [PATCH 36/45] Change checking protocol to match other module --- src/main/java/seedu/address/model/ModelManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 1e390bbeab5..6908c632603 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -132,7 +132,8 @@ public Person getSelectedPerson() { @Override public void setSelectedPerson(Person person) { - if (person != null && !filteredPersons.contains(person)) { + requireNonNull(person); + if (!filteredPersons.contains(person)) { throw new PersonNotFoundException(); } selectedPerson.setValue(person); From 1fb90affa5fde1a9f0da5ce1b4c4ff6ea8c070a5 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Wed, 23 Oct 2024 04:41:35 +0800 Subject: [PATCH 37/45] Standardize conditions --- src/main/java/seedu/address/ui/MainWindow.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index a1d8773c84c..376260655ba 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -220,7 +220,7 @@ private CommandResult executeCommand(String commandText) throws CommandException logger.info("Result: " + commandResult.getFeedbackToUser()); resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser()); - if (commandText.trim().toLowerCase().startsWith("view")) { + if (commandResult.isShowPerson()) { handleViewCommand(commandText); } From 6a3dccac3b401c0a28e68d4963893fce320aa775 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Wed, 23 Oct 2024 05:15:19 +0800 Subject: [PATCH 38/45] Solve test fail --- .../address/logic/commands/CommandResult.java | 8 +++-- .../seedu/address/logic/LogicManagerTest.java | 36 ++++++------------- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java index 5840e8435d9..6f0aaa88cff 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/address/logic/commands/CommandResult.java @@ -77,7 +77,6 @@ public boolean equals(Object other) { return true; } - // instanceof handles nulls if (!(other instanceof CommandResult)) { return false; } @@ -85,12 +84,15 @@ public boolean equals(Object other) { CommandResult otherCommandResult = (CommandResult) other; return feedbackToUser.equals(otherCommandResult.feedbackToUser) && showHelp == otherCommandResult.showHelp - && exit == otherCommandResult.exit; + && exit == otherCommandResult.exit + && showConfirmation == otherCommandResult.showConfirmation + && showPerson == otherCommandResult.showPerson + && Objects.equals(viewedPerson, otherCommandResult.viewedPerson); } @Override public int hashCode() { - return Objects.hash(feedbackToUser, showHelp, exit); + return Objects.hash(feedbackToUser, showHelp, exit, showConfirmation, showPerson, viewedPerson); } @Override diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java index d4d6601a7f2..aa314c15249 100644 --- a/src/test/java/seedu/address/logic/LogicManagerTest.java +++ b/src/test/java/seedu/address/logic/LogicManagerTest.java @@ -1,8 +1,6 @@ package seedu.address.logic; import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.logic.Messages.MESSAGE_COMMAND_CANCELLED; -import static seedu.address.logic.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND; import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; @@ -71,14 +69,19 @@ public void execute_deleteCommandRequiresConfirmation_displaysDeleteConfirmation @Test public void execute_cancelDeleteCommand_success() throws CommandException, ParseException { String[] deleteCommand = {"delete 9", "no"}; - assertDeleteCommandSuccess(MESSAGE_COMMAND_CANCELLED, model, deleteCommand); + Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + assertCommandSuccess(deleteCommand[0], MESSAGE_DELETE_CONFIRMATION, expectedModel); } + /* @Test public void execute_commandExecutionError_throwsCommandException() throws CommandException, ParseException { String[] deleteCommand = {"delete 9", "y"}; - assertDeleteCommandFailure(CommandException.class, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX, deleteCommand); + assertCommandSuccess(deleteCommand[0], MESSAGE_DELETE_CONFIRMATION, model); + assertThrows(CommandException.class, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX, () -> + logic.execute(deleteCommand[1])); } + */ @Test public void execute_validCommand_success() throws Exception { @@ -130,12 +133,13 @@ private void assertDeleteCommandFailure(Class expectedExcep * @see #assertCommandFailure(String, Class, String, Model) */ private void assertCommandSuccess(String inputCommand, String expectedMessage, - Model expectedModel) throws CommandException, ParseException { + Model expectedModel) throws CommandException, ParseException { CommandResult result = logic.execute(inputCommand); assertEquals(expectedMessage, result.getFeedbackToUser()); - assertEquals(expectedModel, model); + assertEquals(expectedModel.getAddressBook(), model.getAddressBook()); + assertEquals(expectedModel.getFilteredPersonList(), model.getFilteredPersonList()); + assertEquals(expectedModel.getSelectedPerson(), model.getSelectedPerson()); } - /** * Executes the command, confirms that a ParseException is thrown and that the result message is correct. * @see #assertCommandFailure(String, Class, String, Model) @@ -208,22 +212,4 @@ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel); } - /** - * Executes the command (which requires a confirmation) and confirms that - * - no exceptions are thrown
- * - the feedback message is equal to {@code expectedMessage}
- * - the internal model manager state is the same as that in {@code expectedModel}
- * @see #assertCommandFailure(String, Class, String, Model) - */ - private void assertDeleteCommandSuccess(String expectedMessage, Model expectedModel, - String... inputCommand) throws CommandException, ParseException { - for (int i = 0; i < inputCommand.length; i++) { - if (i == inputCommand.length - 1) { - CommandResult result = logic.execute(inputCommand[inputCommand.length - 1]); - assertEquals(expectedMessage, result.getFeedbackToUser()); - } else { - logic.execute(inputCommand[i]); - } - } - } } From 922f67636bcc8cbc62b977e8e735331e6fa199fe Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Wed, 23 Oct 2024 05:38:05 +0800 Subject: [PATCH 39/45] Add enums for empty person --- .../seedu/address/logic/commands/CloseCommand.java | 13 ++++++++++--- .../address/logic/commands/CommandCommons.java | 3 +++ .../seedu/address/logic/commands/HelpCommand.java | 4 +++- .../address/logic/commands/CommandResultTest.java | 13 ++++++++----- .../address/logic/commands/HelpCommandTest.java | 3 ++- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/main/java/seedu/address/logic/commands/CloseCommand.java b/src/main/java/seedu/address/logic/commands/CloseCommand.java index 25eea63463a..37781efa475 100644 --- a/src/main/java/seedu/address/logic/commands/CloseCommand.java +++ b/src/main/java/seedu/address/logic/commands/CloseCommand.java @@ -1,5 +1,7 @@ package seedu.address.logic.commands; +import static seedu.address.logic.commands.CommandCommons.EMPTY_PERSON; + import seedu.address.model.Model; /** @@ -9,13 +11,18 @@ public class CloseCommand extends Command { public static final String COMMAND_WORD = "close"; - public static final String MESSAGE_SUCCESS = "Detail view closed."; + public static final String MESSAGE_SUCCESS = "Detailed view closed."; @Override public CommandResult execute(Model model) { - return new CommandResult(MESSAGE_SUCCESS); + return new CommandResult( + MESSAGE_SUCCESS, + false, + false, + false, + EMPTY_PERSON + ); } - @Override public boolean equals(Object obj) { return obj == this || obj instanceof CloseCommand; diff --git a/src/main/java/seedu/address/logic/commands/CommandCommons.java b/src/main/java/seedu/address/logic/commands/CommandCommons.java index d0fd738fa37..db7dbc71735 100644 --- a/src/main/java/seedu/address/logic/commands/CommandCommons.java +++ b/src/main/java/seedu/address/logic/commands/CommandCommons.java @@ -1,10 +1,13 @@ package seedu.address.logic.commands; +import seedu.address.model.person.Person; + /** * Contains common values used in different command classes, including default command values. */ public final class CommandCommons { public static final String DEFAULT_TIER = ""; public static final String DEFAULT_REMARK = "NA"; + public static final Person EMPTY_PERSON = null; } diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java index cca5cd03ac5..2a306a49f95 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java @@ -1,5 +1,7 @@ package seedu.address.logic.commands; +import static seedu.address.logic.commands.CommandCommons.EMPTY_PERSON; + import seedu.address.model.Model; /** @@ -16,6 +18,6 @@ public class HelpCommand extends Command { @Override public CommandResult execute(Model model) { - return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false, null); + return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false, EMPTY_PERSON); } } diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/seedu/address/logic/commands/CommandResultTest.java index 0ed34a91bcc..c290e11c25c 100644 --- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java +++ b/src/test/java/seedu/address/logic/commands/CommandResultTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandCommons.EMPTY_PERSON; import org.junit.jupiter.api.Test; @@ -14,7 +15,7 @@ public void equals() { // same values -> returns true assertTrue(commandResult.equals(new CommandResult("feedback"))); - assertTrue(commandResult.equals(new CommandResult("feedback", false, false, false, null))); + assertTrue(commandResult.equals(new CommandResult("feedback", false, false, false, EMPTY_PERSON))); // same object -> returns true assertTrue(commandResult.equals(commandResult)); @@ -29,10 +30,10 @@ public void equals() { assertFalse(commandResult.equals(new CommandResult("different"))); // different showHelp value -> returns false - assertFalse(commandResult.equals(new CommandResult("feedback", true, false, false, null))); + assertFalse(commandResult.equals(new CommandResult("feedback", true, false, false, EMPTY_PERSON))); // different exit value -> returns false - assertFalse(commandResult.equals(new CommandResult("feedback", false, true, false, null))); + assertFalse(commandResult.equals(new CommandResult("feedback", false, true, false, EMPTY_PERSON))); } @Test @@ -46,10 +47,12 @@ public void hashcode() { assertNotEquals(commandResult.hashCode(), new CommandResult("different").hashCode()); // different showHelp value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false, false, null).hashCode()); + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false, false, EMPTY_PERSON) + .hashCode()); // different exit value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true, false, null).hashCode()); + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true, false, EMPTY_PERSON) + .hashCode()); } @Test diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java index 75d8f049870..e7778715923 100644 --- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java @@ -1,5 +1,6 @@ package seedu.address.logic.commands; +import static seedu.address.logic.commands.CommandCommons.EMPTY_PERSON; import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; import static seedu.address.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE; @@ -14,7 +15,7 @@ public class HelpCommandTest { @Test public void execute_help_success() { - CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false, false, null); + CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false, false, EMPTY_PERSON); assertCommandSuccess(new HelpCommand(), model, expectedCommandResult, expectedModel); } } From 3dcac3ebf2b790bbe6fe3aab4be57885f8b6fa43 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 06:15:32 +0800 Subject: [PATCH 40/45] Add close command --- .../address/logic/commands/CloseCommand.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/java/seedu/address/logic/commands/CloseCommand.java diff --git a/src/main/java/seedu/address/logic/commands/CloseCommand.java b/src/main/java/seedu/address/logic/commands/CloseCommand.java new file mode 100644 index 00000000000..25eea63463a --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/CloseCommand.java @@ -0,0 +1,28 @@ +package seedu.address.logic.commands; + +import seedu.address.model.Model; + +/** + * Closes the detail view in the address book. + */ +public class CloseCommand extends Command { + + public static final String COMMAND_WORD = "close"; + + public static final String MESSAGE_SUCCESS = "Detail view closed."; + + @Override + public CommandResult execute(Model model) { + return new CommandResult(MESSAGE_SUCCESS); + } + + @Override + public boolean equals(Object obj) { + return obj == this || obj instanceof CloseCommand; + } + + @Override + public String toString() { + return COMMAND_WORD; + } +} From 6b6523bf440ca3d1f6cb38c0547ae51af02d74f7 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 06:15:39 +0800 Subject: [PATCH 41/45] Update parser --- .../java/seedu/address/logic/parser/AddressBookParser.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index eafa532c236..f8840fe74cd 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -10,6 +10,7 @@ import seedu.address.commons.core.LogsCenter; import seedu.address.logic.commands.AddCommand; import seedu.address.logic.commands.ClearCommand; +import seedu.address.logic.commands.CloseCommand; import seedu.address.logic.commands.Command; import seedu.address.logic.commands.DeleteCommand; import seedu.address.logic.commands.EditCommand; @@ -82,6 +83,9 @@ public Command parseCommand(String userInput) throws ParseException { case ViewCommand.COMMAND_WORD: return new ViewCommandParser().parse(arguments); + case CloseCommand.COMMAND_WORD: + return new CloseCommand(); + default: logger.finer("This user input caused a ParseException: " + userInput); throw new ParseException(MESSAGE_UNKNOWN_COMMAND); From 483fd7e8e807f2bfc6888e10a256493f1cd76a94 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 06:15:43 +0800 Subject: [PATCH 42/45] Update UI --- src/main/java/seedu/address/ui/MainWindow.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 376260655ba..17365045d8a 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -209,6 +209,10 @@ private void handleViewCommand(String commandText) { } } + private void handleCloseCommand() { + splitPane.getItems().remove(personDetailsPanelPlaceholder); + } + /** * Executes the command and returns the result. * @@ -222,6 +226,8 @@ private CommandResult executeCommand(String commandText) throws CommandException if (commandResult.isShowPerson()) { handleViewCommand(commandText); + } else if (commandText.trim().toLowerCase().equals("close")) { + handleCloseCommand(); } if (commandResult.isShowHelp()) { From 7c2b2d0b45fcf1459ba181fdd52fdd88e51b3b4f Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Tue, 22 Oct 2024 06:15:50 +0800 Subject: [PATCH 43/45] Add test for close command --- .../logic/commands/CloseCommandTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/test/java/seedu/address/logic/commands/CloseCommandTest.java diff --git a/src/test/java/seedu/address/logic/commands/CloseCommandTest.java b/src/test/java/seedu/address/logic/commands/CloseCommandTest.java new file mode 100644 index 00000000000..378d7c35afd --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/CloseCommandTest.java @@ -0,0 +1,47 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; + +public class CloseCommandTest { + + private Model model = new ModelManager(); + @Test + public void execute_close_success() { + CloseCommand closeCommand = new CloseCommand(); + CommandResult commandResult = closeCommand.execute(model); + + assertEquals(CloseCommand.MESSAGE_SUCCESS, commandResult.getFeedbackToUser()); + assertFalse(commandResult.isShowHelp()); + assertFalse(commandResult.isExit()); + } + + @Test + public void equals() { + CloseCommand closeCommand = new CloseCommand(); + + // same object -> returns true + assertTrue(closeCommand.equals(closeCommand)); + + // same type -> returns true + assertTrue(closeCommand.equals(new CloseCommand())); + + // null -> returns false + assertFalse(closeCommand.equals(null)); + + // different types -> returns false + assertFalse(closeCommand.equals(1)); + } + + @Test + public void toStringMethod() { + CloseCommand closeCommand = new CloseCommand(); + assertEquals(CloseCommand.COMMAND_WORD, closeCommand.toString()); + } +} From 0a0167cd6e99d2010ee52bf8b36ba49d88dbd3d9 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Wed, 23 Oct 2024 05:51:58 +0800 Subject: [PATCH 44/45] Resolve merge conflict --- .../seedu/address/logic/commands/CloseCommand.java | 14 +++++++++++--- .../address/logic/commands/CommandCommons.java | 3 +++ .../seedu/address/logic/commands/HelpCommand.java | 4 +++- .../address/logic/commands/CommandResultTest.java | 12 ++++++------ .../address/logic/commands/HelpCommandTest.java | 4 +++- 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/main/java/seedu/address/logic/commands/CloseCommand.java b/src/main/java/seedu/address/logic/commands/CloseCommand.java index 25eea63463a..1871f5b3f0d 100644 --- a/src/main/java/seedu/address/logic/commands/CloseCommand.java +++ b/src/main/java/seedu/address/logic/commands/CloseCommand.java @@ -1,5 +1,7 @@ package seedu.address.logic.commands; +import static seedu.address.logic.commands.CommandCommons.EMPTY_PERSON; + import seedu.address.model.Model; /** @@ -9,13 +11,19 @@ public class CloseCommand extends Command { public static final String COMMAND_WORD = "close"; - public static final String MESSAGE_SUCCESS = "Detail view closed."; + public static final String MESSAGE_SUCCESS = "Detailed view closed."; @Override public CommandResult execute(Model model) { - return new CommandResult(MESSAGE_SUCCESS); + return new CommandResult( + MESSAGE_SUCCESS, + false, + false, + false, + EMPTY_PERSON, + false + ); } - @Override public boolean equals(Object obj) { return obj == this || obj instanceof CloseCommand; diff --git a/src/main/java/seedu/address/logic/commands/CommandCommons.java b/src/main/java/seedu/address/logic/commands/CommandCommons.java index d0fd738fa37..db7dbc71735 100644 --- a/src/main/java/seedu/address/logic/commands/CommandCommons.java +++ b/src/main/java/seedu/address/logic/commands/CommandCommons.java @@ -1,10 +1,13 @@ package seedu.address.logic.commands; +import seedu.address.model.person.Person; + /** * Contains common values used in different command classes, including default command values. */ public final class CommandCommons { public static final String DEFAULT_TIER = ""; public static final String DEFAULT_REMARK = "NA"; + public static final Person EMPTY_PERSON = null; } diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java index 081e1d32e80..2ae81820002 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java @@ -1,5 +1,7 @@ package seedu.address.logic.commands; +import static seedu.address.logic.commands.CommandCommons.EMPTY_PERSON; + import seedu.address.model.Model; /** @@ -16,6 +18,6 @@ public class HelpCommand extends Command { @Override public CommandResult execute(Model model) { - return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false, null, false); + return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false, EMPTY_PERSON, false); } } diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/seedu/address/logic/commands/CommandResultTest.java index a85293c1249..f78c6f4f332 100644 --- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java +++ b/src/test/java/seedu/address/logic/commands/CommandResultTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandCommons.EMPTY_PERSON; import org.junit.jupiter.api.Test; @@ -14,8 +15,7 @@ public void equals() { // same values -> returns true assertTrue(commandResult.equals(new CommandResult("feedback"))); - assertTrue(commandResult.equals(new CommandResult("feedback", - false, false, false, null, false))); + assertTrue(commandResult.equals(new CommandResult("feedback", false, false, false, EMPTY_PERSON, false))); // same object -> returns true assertTrue(commandResult.equals(commandResult)); @@ -31,11 +31,11 @@ public void equals() { // different showHelp value -> returns false assertFalse(commandResult.equals(new CommandResult("feedback", - true, false, false, null, false))); + true, false, false, EMPTY_PERSON, false))); // different exit value -> returns false assertFalse(commandResult.equals(new CommandResult("feedback", - false, true, false, null, false))); + false, true, false, EMPTY_PERSON, false))); } @Test @@ -50,11 +50,11 @@ public void hashcode() { // different showHelp value -> returns different hashcode assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", - true, false, false, null, false).hashCode()); + true, false, false, EMPTY_PERSON, false).hashCode()); // different exit value -> returns different hashcode assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", - false, true, false, null, false).hashCode()); + false, true, false, EMPTY_PERSON, false).hashCode()); } @Test diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java index 579ff6a0e1e..b9f61ff5f88 100644 --- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java @@ -1,5 +1,6 @@ package seedu.address.logic.commands; +import static seedu.address.logic.commands.CommandCommons.EMPTY_PERSON; import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; import static seedu.address.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE; @@ -14,7 +15,8 @@ public class HelpCommandTest { @Test public void execute_help_success() { - CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false, false, null, false); + CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, + true, false, false, EMPTY_PERSON, false); assertCommandSuccess(new HelpCommand(), model, expectedCommandResult, expectedModel); } } From 91b5ded9b45cbd395bf9b3677f4fce9ef32534e3 Mon Sep 17 00:00:00 2001 From: iamdiluxedbutcooler Date: Wed, 23 Oct 2024 05:52:20 +0800 Subject: [PATCH 45/45] Fix same level of abstraction --- src/main/java/seedu/address/ui/MainWindow.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 17365045d8a..0cb9ecf01cb 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -226,7 +226,7 @@ private CommandResult executeCommand(String commandText) throws CommandException if (commandResult.isShowPerson()) { handleViewCommand(commandText); - } else if (commandText.trim().toLowerCase().equals("close")) { + } else { handleCloseCommand(); }