diff --git a/docs/UserGuide.md b/docs/UserGuide.md index c8b51dab479..3f53a690115 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -122,11 +122,11 @@ Examples: Adds a note to a contact from the contact list. -Format: `add note -n NAME -t NOTE_TITLE -c NOTE_CONTENT` +Format: `add note -id CONTACT_ID -tit NOTE_TITLE -con NOTE_CONTENT` Examples: -* `add note -n Aaron -t Meeting Topics -c The topic is about the framework design of the project` -* `add note -n Daniel -t Open Position -e Applications for SWE full-time positions will open soon` +* `add note -id 1 -tit Meeting Topics -con The topic is about the framework design of the project` +* `add note -id 2 -tit Open Position -con Applications for SWE full-time positions will open soon` ### Listing all notes : `list notes` @@ -138,12 +138,12 @@ Format: `list notes` Deletes the specified note from the contact list. -Format: `delete note -n NAME -t NOTE_TITLE` +Format: `delete note -id CONTACT_ID -nid NOTE_ID` -* Deletes the note with title `NOTE_TITLE` from the contact with name `NAME`. +* Deletes the note with the id `NOTE_ID` from the contact with id `CONTACT_ID`. Examples: -* `list note` followed by `delete note -n Aaron -t Meeting Topics` deletes the note with title Meeting Topics from the contact with name Aaron. +* `list note` followed by `delete note -id 1 -nid 1` deletes the note with title Meeting Topics from the contact with name Aaron. ### Adding events: `add event` diff --git a/src/main/java/seedu/address/logic/commands/AddNoteCommand.java b/src/main/java/seedu/address/logic/commands/AddNoteCommand.java new file mode 100644 index 00000000000..1ad4d7b83f2 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddNoteCommand.java @@ -0,0 +1,45 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.note.Note; +import seedu.address.model.person.Person; + +/** + * The command handler for {@code add note} command + */ +public class AddNoteCommand extends AddCommand { + public static final String SECONDARY_COMMAND_WORD = "note"; + public static final String MESSAGE_SUCCESS = "New note added: "; + + public static final String MESSAGE_USAGE = COMMAND_WORD + " " + SECONDARY_COMMAND_WORD + + ": Adds a note to a contact from the contact list.\n" + + "Usage: add note -id CONTACT_ID -tit NOTE_TITLE -con NOTE_CONTENT"; + public static final String MESSAGE_PERSON_NOT_FOUND = "Can not find the target contact with ID: "; + + private final Note toAdd; + private final int contactId; + + /** + * Creates an AddNoteCommand to add the specified {@code Note} + */ + public AddNoteCommand(int contactId, Note note) { + requireNonNull(note); + this.contactId = contactId; + this.toAdd = note; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + Person person = model.findPersonByUserFriendlyId(this.contactId); + if (person == null) { + throw new CommandException(MESSAGE_PERSON_NOT_FOUND + this.contactId); + } + person.addNote(this.toAdd); + + return new CommandResult(MESSAGE_SUCCESS + toAdd.getTitle()); + } +} diff --git a/src/main/java/seedu/address/logic/commands/DeleteNoteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteNoteCommand.java new file mode 100644 index 00000000000..298c9493f8a --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeleteNoteCommand.java @@ -0,0 +1,45 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.person.Person; + +/** + * The command handler for {@code delete note} command + */ +public class DeleteNoteCommand extends DeleteCommand { + public static final String SECONDARY_COMMAND_WORD = "note"; + public static final String MESSAGE_USAGE = COMMAND_WORD + " " + + SECONDARY_COMMAND_WORD + ": Deletes a note from a contact.\n" + + "Usage: delete note -id CONTACT_ID -nid NOTE_ID"; + public static final String MESSAGE_PERSON_NOT_FOUND = "Can not find the target contact with ID: "; + public static final String MESSAGE_SUCCESS = "Successfully deleted note: "; + public static final String MESSAGE_NOTE_NOT_FOUND = "Note not found: ID = "; + + private final int noteIdToDelete; + private final int contactId; + /** + * Creates an DeleteEventCommand to delete the specified {@code Event} + */ + public DeleteNoteCommand(int contactId, int noteIdToDelete) { + this.contactId = contactId; + this.noteIdToDelete = noteIdToDelete; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + Person person = model.findPersonByUserFriendlyId(this.contactId); + if (person == null) { + throw new CommandException(MESSAGE_PERSON_NOT_FOUND + this.contactId); + } + boolean success = person.removeNoteByUserFriendlyId(this.noteIdToDelete); + if (!success) { + throw new CommandException(MESSAGE_NOTE_NOT_FOUND + this.noteIdToDelete); + } + + return new CommandResult(MESSAGE_SUCCESS + this.noteIdToDelete); + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java index b82e68cb16e..ecae8d1be41 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java @@ -4,6 +4,7 @@ import seedu.address.logic.commands.AddCommand; import seedu.address.logic.commands.AddEventCommand; +import seedu.address.logic.commands.AddNoteCommand; import seedu.address.logic.commands.AddPersonCommand; import seedu.address.logic.parser.exceptions.ParseException; @@ -21,6 +22,8 @@ public AddCommand parse(String userInput) throws ParseException { return new AddPersonCommandParser().parse(args); case AddEventCommand.SECONDARY_COMMAND_WORD: return new AddEventCommandParser().parse(args); + case AddNoteCommand.SECONDARY_COMMAND_WORD: + return new AddNoteCommandParser().parse(args); default: throw new ParseException(MESSAGE_UNKNOWN_COMMAND); } diff --git a/src/main/java/seedu/address/logic/parser/AddNoteCommandParser.java b/src/main/java/seedu/address/logic/parser/AddNoteCommandParser.java new file mode 100644 index 00000000000..13e15df79aa --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AddNoteCommandParser.java @@ -0,0 +1,42 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.Messages.MESSAGE_INVALID_INTEGER_ARGUMENT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE_CONTENT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE_TITLE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PERSON_ID; + +import seedu.address.logic.commands.AddNoteCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.note.Note; + +/** + * Parses input arguments and creates a new AddNoteCommand object + */ +public class AddNoteCommandParser implements Parser { + @Override + public AddNoteCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_PERSON_ID, + PREFIX_NOTE_TITLE, PREFIX_NOTE_CONTENT); + + if (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PERSON_ID, PREFIX_NOTE_TITLE, PREFIX_NOTE_CONTENT) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddNoteCommand.MESSAGE_USAGE)); + } + + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_PERSON_ID, PREFIX_NOTE_TITLE, PREFIX_NOTE_CONTENT); + + String noteTitle = argMultimap.getValue(PREFIX_NOTE_TITLE).get(); + String noteContent = argMultimap.getValue(PREFIX_NOTE_CONTENT).get(); + Note newNote = new Note(noteTitle, noteContent); + + int contactId = -1; + try { + contactId = Integer.parseInt(argMultimap.getValue(PREFIX_PERSON_ID).get()); + } catch (NumberFormatException e) { + throw new ParseException(String.format(MESSAGE_INVALID_INTEGER_ARGUMENT, e.getMessage())); + } + + return new AddNoteCommand(contactId, newNote); + } +} diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index c4cf5544c7d..64aa6467d35 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -8,11 +8,14 @@ public class CliSyntax { /* Prefix definitions */ public static final Prefix PREFIX_NAME = new Prefix("-n"); public static final Prefix PREFIX_PERSON_ID = new Prefix("-id"); + public static final Prefix PREFIX_NOTE_ID = new Prefix("-nid"); public static final Prefix PREFIX_EVENT_ID = new Prefix("-eid"); public static final Prefix PREFIX_PHONE = new Prefix("-p"); public static final Prefix PREFIX_EMAIL = new Prefix("-e"); public static final Prefix PREFIX_ADDRESS = new Prefix("-a"); public static final Prefix PREFIX_TAG = new Prefix("-t"); + public static final Prefix PREFIX_NOTE_TITLE = new Prefix("-tit"); + public static final Prefix PREFIX_NOTE_CONTENT = new Prefix("-con"); public static final Prefix PREFIX_EVENT_NAME = new Prefix("-en"); public static final Prefix PREFIX_EVENT_START_TIME = new Prefix("-st"); public static final Prefix PREFIX_EVENT_END_TIME = new Prefix("-et"); diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java index 03538c33ea3..8d5c3c6d943 100644 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java @@ -4,6 +4,7 @@ import seedu.address.logic.commands.DeleteCommand; import seedu.address.logic.commands.DeleteEventCommand; +import seedu.address.logic.commands.DeleteNoteCommand; import seedu.address.logic.commands.DeletePersonCommand; import seedu.address.logic.parser.exceptions.ParseException; @@ -18,6 +19,8 @@ public DeleteCommand parse(String userInput) throws ParseException { switch (secondaryCommandWord) { case DeletePersonCommand.SECONDARY_COMMAND_WORD: return new DeletePersonCommandParser().parse(args); + case DeleteNoteCommand.SECONDARY_COMMAND_WORD: + return new DeleteNoteCommandParser().parse(args); case DeleteEventCommand.SECONDARY_COMMAND_WORD: return new DeleteEventCommandParser().parse(args); default: diff --git a/src/main/java/seedu/address/logic/parser/DeleteNoteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteNoteCommandParser.java new file mode 100644 index 00000000000..75d13e23229 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/DeleteNoteCommandParser.java @@ -0,0 +1,36 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.Messages.MESSAGE_INVALID_INTEGER_ARGUMENT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE_ID; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PERSON_ID; + +import seedu.address.logic.commands.DeleteNoteCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteNoteCommand object + */ +public class DeleteNoteCommandParser implements Parser { + @Override + public DeleteNoteCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_PERSON_ID, PREFIX_NOTE_ID); + + if (!ParserUtil.arePrefixesPresent(argMultimap, PREFIX_PERSON_ID, PREFIX_NOTE_ID) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteNoteCommand.MESSAGE_USAGE)); + } + + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_PERSON_ID, PREFIX_NOTE_ID); + int contactId = -1; + int noteId = -1; + try { + contactId = Integer.parseInt(argMultimap.getValue(PREFIX_PERSON_ID).get()); + noteId = Integer.parseInt(argMultimap.getValue(PREFIX_NOTE_ID).get()); + } catch (NumberFormatException e) { + throw new ParseException(String.format(MESSAGE_INVALID_INTEGER_ARGUMENT, e.getMessage())); + } + + return new DeleteNoteCommand(contactId, noteId); + } +} diff --git a/src/main/java/seedu/address/model/note/Note.java b/src/main/java/seedu/address/model/note/Note.java index aee1a84d075..bef49806269 100644 --- a/src/main/java/seedu/address/model/note/Note.java +++ b/src/main/java/seedu/address/model/note/Note.java @@ -1,27 +1,44 @@ package seedu.address.model.note; /** - * The class for holding a Note + * Represents a Note. */ public class Note { - private final String title; - private final String body; + private final NoteTitle title; + private final NoteContent content; /** - * The constructor for the Note class - * @param title The title of the note - * @param body The body of the note + * Constructs a Note. + * @param title The title of the note. + * @param content The content of the note. */ - public Note(String title, String body) { - this.title = title; - this.body = body; + public Note(String title, String content) { + this.title = NoteTitle.fromString(title); + this.content = NoteContent.fromString(content); } + /** + * Returns the title of the note. + * @return The title of the note. + */ public String getTitle() { - return title; + return title.toString(); + } + + /** + * Returns the content of the note. + * @return The content of the note. + */ + public String getContent() { + return content.toString(); } - public String getBody() { - return body; + /** + * Returns a string that can be used to represent this note on GUI. + * @return The information in string. + */ + public String getUiText() { + String result = this.getTitle() + " (content: " + this.getContent() + ")"; + return result; } } diff --git a/src/main/java/seedu/address/model/note/NoteContent.java b/src/main/java/seedu/address/model/note/NoteContent.java new file mode 100644 index 00000000000..530623e0d51 --- /dev/null +++ b/src/main/java/seedu/address/model/note/NoteContent.java @@ -0,0 +1,25 @@ +package seedu.address.model.note; + +/** + * Represents a Note's content. + */ +public class NoteContent { + private final String content; + + private NoteContent(String content) { + this.content = content; + } + + /** + * Constructs a NoteContent. + * @param content The content of the note. + */ + public static NoteContent fromString(String content) { + return new NoteContent(content); + } + + @Override + public String toString() { + return this.content; + } +} diff --git a/src/main/java/seedu/address/model/note/NoteTitle.java b/src/main/java/seedu/address/model/note/NoteTitle.java new file mode 100644 index 00000000000..5564d1066bd --- /dev/null +++ b/src/main/java/seedu/address/model/note/NoteTitle.java @@ -0,0 +1,25 @@ +package seedu.address.model.note; + +/** + * Represents a Note's title. + */ +public class NoteTitle { + private final String title; + + private NoteTitle(String title) { + this.title = title; + } + + /** + * Constructs a NoteTitle. + * @param title The title of the note. + */ + public static NoteTitle fromString(String title) { + return new NoteTitle(title); + } + + @Override + public String toString() { + return this.title; + } +} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java index 0680752a62c..3f56f0a21d2 100644 --- a/src/main/java/seedu/address/model/person/Person.java +++ b/src/main/java/seedu/address/model/person/Person.java @@ -2,7 +2,6 @@ import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; -import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -31,7 +30,7 @@ public class Person { // Data fields private final Address address; private final Set tags = new HashSet<>(); - private final List notes = new ArrayList<>(); + private final ObservableList notes = FXCollections.observableArrayList(); private final ObservableList events = FXCollections.observableArrayList(); /** @@ -79,8 +78,8 @@ public Set getTags() { * {@code UnsupportedOperationException} * if modification is attempted. */ - public List getNotes() { - return Collections.unmodifiableList(notes); + public ObservableList getNotes() { + return FXCollections.unmodifiableObservableList(notes); } /** @@ -92,6 +91,38 @@ public ObservableList getEvents() { return FXCollections.unmodifiableObservableList(events); } + /** + * Adds a note to this person + * @param note The note to be added. + */ + public void addNote(Note note) { + this.notes.add(note); + } + + /** + * Remove a note by its user-friendly id + * @param id The id of the note you want to remove + * @return {@code true} if the operation is successful and {@code false} if the note with this id does not exist + */ + public boolean removeNoteByUserFriendlyId(int id) { + return this.removeNoteByIndex(id - 1); + } + + private boolean removeNoteByIndex(int index) { + if (index < 0 || index >= this.notes.size()) { + return false; + } + this.notes.remove(index); + return true; + } + + /** + * Adds an event to this person. + * @param event The event to be added. + */ + public void addEvent(Event event) { + this.events.add(event); + } /** * Remove an event by its user-friendly id @@ -162,12 +193,4 @@ public String toString() { .add("tags", tags) .toString(); } - - /** - * Add an event to this person - */ - public void addEvent(Event event) { - this.events.add(event); - } - } diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index 2e393525f7c..92d1e04fd14 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -25,7 +25,7 @@ public class SampleDataUtil { public static Person[] getSamplePersons() { ArrayList sampleNotes = new ArrayList(); - sampleNotes.add(new Note("Hello", "Sample body")); + sampleNotes.add(new Note("Hello", "Sample content")); ArrayList sampleEvents = new ArrayList(); sampleEvents.add(new Event("Sample event", LocalDateTime.now() diff --git a/src/main/java/seedu/address/storage/JsonAdaptedNote.java b/src/main/java/seedu/address/storage/JsonAdaptedNote.java index abe9e75921e..dd3ba79e579 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedNote.java +++ b/src/main/java/seedu/address/storage/JsonAdaptedNote.java @@ -13,15 +13,15 @@ public class JsonAdaptedNote { public static final String MISSING_FIELD_MESSAGE_FORMAT = "Notes's %s field is missing!"; private final String title; - private final String body; + private final String content; /** * Constructs a {@code JsonAdaptedNote} with the given person details. */ @JsonCreator - public JsonAdaptedNote(@JsonProperty("title") String title, @JsonProperty("body") String body) { + public JsonAdaptedNote(@JsonProperty("title") String title, @JsonProperty("content") String content) { this.title = title; - this.body = body; + this.content = content; } /** @@ -29,7 +29,7 @@ public JsonAdaptedNote(@JsonProperty("title") String title, @JsonProperty("body" */ public JsonAdaptedNote(Note source) { title = source.getTitle(); - body = source.getBody(); + content = source.getContent(); } @@ -46,10 +46,10 @@ public Note toModelType() throws IllegalValueException { throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "title")); } - if (body == null) { + if (content == null) { throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "body")); } - return new Note(title, body); + return new Note(title, content); } } diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java index a9872c002fa..18296174adf 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/seedu/address/ui/PersonCard.java @@ -9,6 +9,7 @@ import javafx.scene.layout.Region; import javafx.scene.paint.Color; import seedu.address.model.event.Event; +import seedu.address.model.note.Note; import seedu.address.model.person.Person; /** @@ -43,6 +44,8 @@ public class PersonCard extends UiPart { @FXML private FlowPane tags; @FXML + private ListView notes; + @FXML private ListView events; /** @@ -56,10 +59,26 @@ public PersonCard(Person person, int displayedIndex) { phone.setText(person.getPhone().value); address.setText(person.getAddress().value); email.setText(person.getEmail().value); + notes.setItems(person.getNotes()); + notes.setCellFactory(cell -> new NoteCell()); events.setItems(person.getEvents()); events.setCellFactory(cell -> new EventCell()); } + private class NoteCell extends ListCell { + @Override + protected void updateItem(Note note, boolean empty) { + super.updateItem(note, empty); + if (empty || note == null) { + setGraphic(null); + setText(null); + } else { + setText("Note " + (getIndex() + 1) + ". " + note.getUiText()); + setTextFill(Color.WHITE); + } + } + } + private class EventCell extends ListCell { @Override protected void updateItem(Event event, boolean empty) { diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml index 683e5e4da26..3270800ef0c 100644 --- a/src/main/resources/view/PersonListCard.fxml +++ b/src/main/resources/view/PersonListCard.fxml @@ -32,6 +32,7 @@