From 00c5c539feba5d967661fce9c4cfd88ef37a4013 Mon Sep 17 00:00:00 2001 From: Wern Date: Mon, 13 Nov 2023 17:04:44 +0800 Subject: [PATCH 01/23] Update DG --- docs/DeveloperGuide.md | 212 +++++++++++++++--- docs/diagrams/AutocompleteClasses.puml | 42 ++++ docs/diagrams/style.puml | 6 + .../autocomplete/AutocompleteGenerator.java | 4 +- .../autocomplete/AutocompleteSupplier.java | 2 +- .../AutocompleteSupplierTest.java | 12 +- 6 files changed, 239 insertions(+), 39 deletions(-) create mode 100644 docs/diagrams/AutocompleteClasses.puml diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index d578c988ed1..6003cb6ef0d 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -241,19 +241,19 @@ Since the organization has to be added to the `AddressBook` before any recruiter * Pros: Computationally less expensive and easier to deal with. * Cons: Since AB3's design was implemented with immutability in mind, making part of `Organization` mutable might cause unwanted bugs or mistakes in other parts of the application. Additionally, overhauling the classes to be mutable would incur huge cost in development time. -### Command Autocompletion +### Command Autocompletion Internals #### Overview Jobby's Command Autocompletion is designed to provide users with intelligent command suggestions and offer autocompletion by analyzing the existing partial command input and the current application state. -Just like programming IDEs, a user may type a prefix subsequence of a long command part, and simply press **TAB** to finish the command using the suggested match. +Just like programming IDEs, a user may type a prefix subsequence of a long command part, and simply press **TAB** to finish the command using the suggested match. For instance, type "sort -tt" and press **TAB** to finish the command as "sort --title". -It consists of several key components: +The internal implementation consists of a few notable components: - **`AutocompleteSupplier`**: - This class is responsible for generating possible flags and values to be used for suggestions. - - It takes an `AutocompleteItemSet` of flags, an optional `FlagValueSupplier` mapped to each flag, and can have corresponding `AutocompleteConstraint` applied to flags. + - It takes an `AutocompleteItemSet`, an optional `FlagValueSupplier` mapped to each flag, and can have corresponding `AutocompleteConstraint`s applied to flags. - It helps determine what flags can be added to an existing command phrase based on constraints and existing flags. - **`AutocompleteGenerator`**: @@ -261,58 +261,184 @@ It consists of several key components: - Users can invoke `AutocompleteGenerator#generateCompletions(command, model)` to get autocomplete suggestions. - It does the hard work of taking the possible values provided by either supplier, performing subsequence fuzzy match, and then "predict" what the user is typing. +For code looking to generation completion results from a command, the `AutocompleteGenerator` class is the only class that needs to be used, and any command that has autocomplete results will have a static constant of their respective `AutocompleteSupplier`. -#### `AutocompleteConstraint` +#### Autocomplete Constraints -The `AutocompleteConstraint` class provides a way to specify rules for autocomplete suggestions. It is a functional interface, so it can be treated as a lambda function. +Autocompletion constraints are defined via the `AutocompleteConstraint` functional interface. It provides a way to specify rules for autocomplete suggestions. -It offers static factory methods for quickly defining common rulesets. Examples include: -- `#oneAmongAllOf(items...)`: Specifies that one of the provided items must be present in the command. -- `#onceForEachOf(items...)`: Ensures that each of the provided items can only appear once in the command. -- `#where(item)#isPrerequisiteFor(dependents...)`: Defines dependencies between items, indicating that certain flags are prerequisites for others. -- `#where(item)#cannotExistAlongsideAnyOf(items...)`: Defines that an item cannot be present when any of the others are present. +```java +@FunctionalInterface +public interface AutocompleteConstraint { + boolean isAllowed(T input, Set existingFlags); +} +``` + +**API Reference:** [AutocompleteConstraint,java](https://github.com/AY2324S1-CS2103T-W08-3/tp/tree/master/src/main/java/seedu/address/logic/autocomplete/components/AutocompleteConstraint.java) + +This interface can be thought of as a lambda function that takes in the *input item* and *existing items*, then returns a boolean value indicating whether the input should be allowed. + +In our autocomplete implementation, we use constraints to define rules for what flags can be added to an existing set of flags. We hence use the type `AutocompleteConstraint`. + +##### Built-in Constraints + +The interface offers static factory methods for quick creation of many common constraints. For example: + +- `#oneAmongAllOf(items...)`: Enforces that at most one of the provided items must be present in the command. +- `#onceForEachOf(items...)`: Enforces that each of the provided items can only appear once in the command. +- `#where(item)#isPrerequisiteFor(dependents...)`: Defines dependencies between items, indicating that certain items are prerequisites before its dependents may appear. + +##### Custom Constraints + +It is possible to declare your own constraints. -#### `AutocompleteItemSet` +Hence, to create a constraint that **all** flags cannot be used more than once, we can simply declare it just like so: -The `AutocompleteItemSet` is a set of flags that retains knowledge of which flags have what rules and constraints. It helps determine which flags can be added to an existing set of flags given the known constraints. +```java +AutocompleteConstraint cannotBeUsedMoreThanOnce = (input, existingItems) -> + !existingFlags.contains(input); +``` + +#### Autocomplete Item Sets + +An autocomplete item set - represented by the `AutocompleteItemSet` class - is a custom set of items with an additional perk: it retains knowledge of which items have what rules and constraints. + +Hence, in our autocomplete implementation, we use `AutocompleteItemSet` to easily store and determine which flags can be added to an existing set of flags given the known constraints. + +**API Reference:** [AutocompleteItemSet.java](https://github.com/AY2324S1-CS2103T-W08-3/tp/tree/master/src/main/java/seedu/address/logic/autocomplete/components/AutocompleteItemSet.java) + +##### Built-in Item Set Factories This dataset can be constructed manually with flags and constraints, but it also offers static factory methods for quick creation of flag sets with common constraints. For example: + - `#oneAmongAllOf(items...)`: Creates a set where at most one out of all the provided items may appear. - `#onceForEachOf(items...)`: Ensures that each of the provided items can appear only once. - `#anyNumberOf(items...)`: Creates a set with the rule that items in the set may appear any number of times. -Additionally, some helper operations are provided in a chainable fashion. For example: -- `#concat(sets...)`: Combines sets together to create complex combinations of flag rules and flags. -- `#addDependents(items...)`: Establishes dependencies between flags. This way, flag may require another flag to exist in order to be used. +##### Helper Chainable Operations + +Some helper operations are provided in a chainable fashion to simplify workflows. For example: + +- `#concat(sets...)`: Combines sets together to create complex combinations of items and their rules. +- `#addDependents(items...)`: Establishes dependencies between items. This way, an item may require another different item to exist in order to be used. - `#addConstraints(constraints...)`: Adds more custom constraints as desired. -Finally, we need a way to compute what items are usable given existing set of items that are present. This class exposes one such method: +##### Usage Example + +Suppose we have a set of flags, some supporting repeated usage (`FLAG_REP_1`, `FLAG_REP_2`), and some that may only be used once (`FLAG_ONCE_1`, `FLAG_ONCE_2`). + +We can create such a set, with all the constraints automatically combined, like so: + +```java +AutocompleteItemSet set = AutocompleteItemSet.concat( + AutocompleteItemSet.anyNumberOf(FLAG_REP_1, FLAG_REP_2), + AutocompleteItemSet.onceForEachOf(FLAG_ONCE_1, FLAG_ONCE_2) +); +``` + +##### Computing Usable Items + +Finally, we need a way to compute what items are usable given existing set of items that are present. `AutocompleteItemSet` exposes one final method that is exactly what we need: + - `#getElementsAfterConsuming(items...)`: Gets the remaining set of elements after "consuming" the given ones. -#### `FlagValueSupplier` +#### Flag Value Suppliers + +In some cases, Jobby should be capable of provide suggestions for flags with preset or known values, such as "`--status pending`", or "`--oid alex_yeoh_inc`". This is where flag value suppliers come in. + +The `FlagValueSupplier` functional interface is a simple one that behaves like a lambda function with one task: Given a **partial command** for a flag and the app's **model**, generate all possible values a flag may have. -The `FlagValueSupplier` interface is a simple one that behaves like a lambda function with one task: Given a partial command for a flag and the app's **model** generate all possible suggestion results. +```java +@FunctionalInterface +public interface FlagValueSupplier extends + BiFunction> { + + Stream apply(PartitionedCommand partialCommand, Model model); +} +``` + +**API Reference:** [FlagValueSupplier.java](https://github.com/AY2324S1-CS2103T-W08-3/tp/tree/master/src/main/java/seedu/address/logic/autocomplete/components/FlagValueSupplier.java) + +With the provided details, it is possible to specify arbitrary suppliers with any data. You can supply a preset list of completions, or even retrieve values from the model itself. -By taking in both the command and the app model, it is possible to specify arbitrary suppliers with any data, even from the model itself, like corresponding Id values when using the `--oid` flag for recruiters. +Accessing the partial command is useful if you'd like to change the results based on the heuristically detected type, such as fields that accept either an `INDEX` or an `ID`. -#### `AutocompleteSupplier` +**Note to developers:** Custom `FlagValueSupplier`s need not actually do any prefix or subsequence matching - that is done automatically at the `AutocompleteGenerator` class later. -The `AutocompleteSupplier` leverages the capabilities of `AutocompleteItemSet` and `FlagValueSupplier`. +#### Partitioning Command Strings -Internally, it uses `AutocompleteItemSet` to determine what flags can be added after a given set of flags has been used in a command. +The `PartitionedCommand` class is a simple wrapper for a command string that has been partitioned into its constituent parts, specifically for the purposes of autocomplete. -This allows it to make suggestions based on constraints like "`--org` cannot exist together with `--rec`." +**API Reference:** [PartitionedCommand.java](https://github.com/AY2324S1-CS2103T-W08-3/tp/tree/master/src/main/java/seedu/address/logic/autocomplete/components/PartitionedCommand.java) -Additionally, it uses `FlagValueSupplier` to provide suggestions for flags with preset values, such as "`--status pending`." +Most notably, it is capable of separating a command into these fundamental parts: -#### `AutocompleteGenerator` +``` +[command name] [middle, untouched text] [autocompletable text] +``` + +For example, given the partial command "`add --org --name Alice --oid ama`", the partitioned command would be: -The `AutocompleteGenerator` serves as a wrapper for autocomplete functionality, regardless of it's source. +``` +[add] [--rec --name Alice --oid ] [ama] +``` -It takes an `AutocompleteSupplier` or a `Supplier` and generates autocomplete suggestions. +It also provides other quick access properties which can be found in the API reference. -Once initialized, users can call `AutocompleteGenerator#generateCompletions(command, model)` to receive suggestions from their partial command input. +#### The Autocomplete Supplier +The `AutocompleteSupplier` leverages the capabilities of `AutocompleteItemSet` and `FlagValueSupplier` together to form a full supplier for a single command. + +**API Reference:** [AutocompleteSupplier.java](https://github.com/AY2324S1-CS2103T-W08-3/tp/tree/master/src/main/java/seedu/address/logic/autocomplete/AutocompleteSupplier.java) + +Internally, it must be initialized with an `AutocompleteItemSet` to determine what flags can be added to a command at any point in time, inclusive of all known restrictions. It is most easily done via the factory method `#from`. + +Additionally, one may optionally assign `FlagValueSupplier`s into `Flag`s by inputting `Map`. This allows the supplier to provide suggestions for flags with preset or known values. + +You may configure both `AutocompleteItemSet` and `Map` in the same constructor call, or use factory and chaining methods to create such a set - refer to publicly exposed API calls for more details. + +##### Usage Example + +Recall the example from earlier where we created an `AutocompleteItemSet`? A way to create a full `AutocompleteSupplier` from that is as follows: + +```java +AutocompleteSupplier supplier = AutocompleteSupplier.from(set); +``` + +We can add more details on an existing supplier by using a configurator. Suppose we have a `FlagValueSupplier` for a status flag. This is how we can add it to the supplier: + +```java +supplier.configureValueMap(map -> map.put(FLAG_STATUS, statusFlagValueSupplier)); +``` + +##### Obtaining Results + +The supplier exposes methods to obtain the possible flags and values: + +- `#getOtherPossibleFlagsAsideFromFlagsPresent(Flags...)`: Gets the remaining set of flags that can be added to the command, given the flags that are already present. + +- `#getValidValuesForFlag(Flag, PartitionedCommand, Model)`: Gets the possible values for a flag, given the partial command and the model. + +This is used by `AutocompleteGenerator` to generate suggestions later. + +#### The Autocomplete Generator + +The `AutocompleteGenerator` is the final stage of the autocompletion generation process. + +It supports generating results based on those supplied by an `AutocompleteSupplier`, or any arbitrary `Supplier`, and generates autocomplete suggestions. + +Once initialized, users can simply call the `#generateCompletions(command, model)` method to receive suggestions from their partial command input. It's that easy! + +**API Reference:** [AutocompleteGenerator.java](https://github.com/ay2324s1-cs2103t-w08-3/tp/tree/master/src/main/java/seedu/address/logic/autocomplete/AutocompleteGenerator.java) + +##### High-level Internal Implementation + +Internally, whenever requested, the `AutocompleteGenerator`: +1. obtains a command's parts with `PartitionedCommand`, +2. uses the given supplier to obtain the results based on the available parts, +3. automatically performs fuzzy (subsequence) matching to filter results, +4. ranks them based on their relevance, +5. and finally returns a stream of autocompleted commands. #### Design Considerations @@ -324,7 +450,6 @@ Jobby's Command Autocompletion to provide context-aware suggestions to users, wh Most notably, it also allows for advanced rulesets to be specified in a human-readable fashion. Take a look at [AddCommand#AUTOCOMPLETE_SUPPLIER](https://github.com/AY2324S1-CS2103T-W08-3/tp/blob/c484696fe4c12d514ad3fb6a71ff2dfea089fe32/src/main/java/seedu/address/logic/commands/AddCommand.java#L47). - ### \[Proposed\] Undo/redo feature #### Proposed Implementation @@ -530,6 +655,33 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli (For all use cases below, the **System** is `Jobby` and the **Actor** is the `user`, unless specified otherwise) +**Use case: Inputting commands with autocomplete** + +**MSS** + +1. User inputs a command partially. +2. Jobby shows a list of possible completions that matches the partial command. +3. User selects a completion from the list. +4. Jobby updates the user input with the selected completion. +5. User repeats from step 1 until the command is complete. + + Use case ends. + +**Extensions** + +* 2a1. Jobby does not have any suggestions to list for the partial command. + + Use case resumes at step 5. + +* 3a. User dismisses the list of suggestions. + + Use case resumes at step 5. + +* 1a. User requests to undo the last completion. + * 1a1. Jobby undoes the last completion, if any. + + Use case resumes at step 2. + **Use case: Add an application** diff --git a/docs/diagrams/AutocompleteClasses.puml b/docs/diagrams/AutocompleteClasses.puml new file mode 100644 index 00000000000..d93b613af14 --- /dev/null +++ b/docs/diagrams/AutocompleteClasses.puml @@ -0,0 +1,42 @@ +@startuml +!include style.puml +skinparam arrowThickness 1.1 +skinparam arrowColor AUTOCOMPLETE_COLOR_T4 +skinparam classBackgroundColor AUTOCOMPLETE_COLOR + +package Autocomplete as AutocompletePackage <> { + class AutocompleteSupplier + class AutocompleteGenerator + class AutocompleteItemSet + class PartitionedCommand + class "<>\nFlagValueSupplier" as FlagValueSupplier + class "<>\nAutocompleteConstraint" as AutocompleteConstraint +} + +package Model as ModelPackage <> { + class "<>\nModel" as Model +} +package "Parser classes" { + class Flag +} + +Class " " as HiddenOutside1 #FFFFFF +HiddenOutside1 .down.> AutocompleteGenerator + +Class " " as HiddenOutside2 #FFFFFF +HiddenOutside2 .right.> AutocompleteSupplier + +AutocompleteGenerator .down.> "1" AutocompleteSupplier : uses > + +AutocompleteSupplier ..> "1" AutocompleteItemSet +AutocompleteSupplier ..> "*" FlagValueSupplier +AutocompleteSupplier ..> Flag + +AutocompleteItemSet ..> "*" AutocompleteConstraint : contains > + +AutocompleteGenerator ..> PartitionedCommand : creates > +FlagValueSupplier .right.> PartitionedCommand : uses > + +FlagValueSupplier ..down.> Model : uses > +PartitionedCommand ..down.> Flag : depends on > +@enduml diff --git a/docs/diagrams/style.puml b/docs/diagrams/style.puml index f7d7347ae84..7770580f0b7 100644 --- a/docs/diagrams/style.puml +++ b/docs/diagrams/style.puml @@ -31,6 +31,12 @@ !define STORAGE_COLOR_T3 #806600 !define STORAGE_COLOR_T2 #544400 +!define AUTOCOMPLETE_COLOR #3333C4 +!define AUTOCOMPLETE_COLOR_T1 #C8C8FA +!define AUTOCOMPLETE_COLOR_T2 #6A6ADC +!define AUTOCOMPLETE_COLOR_T3 #1616B0 +!define AUTOCOMPLETE_COLOR_T4 #101086 + !define USER_COLOR #000000 skinparam Package { diff --git a/src/main/java/seedu/address/logic/autocomplete/AutocompleteGenerator.java b/src/main/java/seedu/address/logic/autocomplete/AutocompleteGenerator.java index d7f32a6b431..5db6a0a6e06 100644 --- a/src/main/java/seedu/address/logic/autocomplete/AutocompleteGenerator.java +++ b/src/main/java/seedu/address/logic/autocomplete/AutocompleteGenerator.java @@ -150,14 +150,14 @@ private static Optional> getPossibleValues( ) { Optional flagString = command.getLastConfirmedFlagString(); if (flagString.isEmpty()) { - return supplier.getValidValues(null, command, model); + return supplier.getValidValuesForFlag(null, command, model); } Optional targetFlag = Flag.findMatch( flagString.get(), supplier.getAllPossibleFlags().toArray(Flag[]::new) ); - return targetFlag.flatMap(f -> supplier.getValidValues(f, command, model)); + return targetFlag.flatMap(f -> supplier.getValidValuesForFlag(f, command, model)); } } diff --git a/src/main/java/seedu/address/logic/autocomplete/AutocompleteSupplier.java b/src/main/java/seedu/address/logic/autocomplete/AutocompleteSupplier.java index b35f68974e8..1c259b387d6 100644 --- a/src/main/java/seedu/address/logic/autocomplete/AutocompleteSupplier.java +++ b/src/main/java/seedu/address/logic/autocomplete/AutocompleteSupplier.java @@ -112,7 +112,7 @@ public Set getOtherPossibleFlagsAsideFromFlagsPresent(Set flagsPrese * @param currentCommand The current command structure. This should not be null. * @param model The model to be supplied for generation. This may be null if the model is unavailable. */ - public Optional> getValidValues(Flag flag, PartitionedCommand currentCommand, Model model) { + public Optional> getValidValuesForFlag(Flag flag, PartitionedCommand currentCommand, Model model) { try { return Optional.ofNullable( this.values.getOrDefault(flag, (c, m) -> Stream.empty()) diff --git a/src/test/java/seedu/address/logic/autocomplete/AutocompleteSupplierTest.java b/src/test/java/seedu/address/logic/autocomplete/AutocompleteSupplierTest.java index c4a41af0b34..26e95d3bae7 100644 --- a/src/test/java/seedu/address/logic/autocomplete/AutocompleteSupplierTest.java +++ b/src/test/java/seedu/address/logic/autocomplete/AutocompleteSupplierTest.java @@ -130,19 +130,19 @@ public void getValidValues() { var emptyCommand = new PartitionedCommand(""); // Should use the lambda's values - assertEquals(LIST_A, supplier.getValidValues(FLAG_A, emptyCommand, null) + assertEquals(LIST_A, supplier.getValidValuesForFlag(FLAG_A, emptyCommand, null) .get().collect(Collectors.toList())); - assertEquals(LIST_B, supplier.getValidValues(FLAG_B, emptyCommand, null) + assertEquals(LIST_B, supplier.getValidValuesForFlag(FLAG_B, emptyCommand, null) .get().collect(Collectors.toList())); - assertEquals(LIST_C, supplier.getValidValues(FLAG_C, emptyCommand, null) + assertEquals(LIST_C, supplier.getValidValuesForFlag(FLAG_C, emptyCommand, null) .get().collect(Collectors.toList())); - assertEquals(LIST_EMPTY, supplier.getValidValues(FLAG_D, emptyCommand, null) + assertEquals(LIST_EMPTY, supplier.getValidValuesForFlag(FLAG_D, emptyCommand, null) .get().collect(Collectors.toList())); - assertEquals(LIST_EMPTY, supplier.getValidValues(FLAG_E, emptyCommand, null) + assertEquals(LIST_EMPTY, supplier.getValidValuesForFlag(FLAG_E, emptyCommand, null) .get().collect(Collectors.toList())); // NPEs should be caught if the lambda does not handle it - assertEquals(LIST_EMPTY, supplier.getValidValues(FLAG_F, emptyCommand, null) + assertEquals(LIST_EMPTY, supplier.getValidValuesForFlag(FLAG_F, emptyCommand, null) .get().collect(Collectors.toList())); } From 5decaa8ca9d83f96ef1dbad3b25b235045a3c3f5 Mon Sep 17 00:00:00 2001 From: Wern Date: Mon, 13 Nov 2023 17:54:15 +0800 Subject: [PATCH 02/23] Update autocomplete puml --- docs/diagrams/AutocompleteClasses.puml | 39 ++++++++++++++++---------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/docs/diagrams/AutocompleteClasses.puml b/docs/diagrams/AutocompleteClasses.puml index d93b613af14..9e893f6beda 100644 --- a/docs/diagrams/AutocompleteClasses.puml +++ b/docs/diagrams/AutocompleteClasses.puml @@ -1,42 +1,51 @@ @startuml !include style.puml -skinparam arrowThickness 1.1 -skinparam arrowColor AUTOCOMPLETE_COLOR_T4 -skinparam classBackgroundColor AUTOCOMPLETE_COLOR +skinparam arrowThickness 1.2 +skinparam arrowColor LOGIC_COLOR_T4 +skinparam classBackgroundColor LOGIC_COLOR + +skinparam class { + 'workaround to render generics properly without breaking the rest + BorderColor LOGIC_COLOR + BorderThickness 0 + StereotypeFontColor LOGIC_COLOR_T2 + StereotypeFontSize 14 +} package Autocomplete as AutocompletePackage <> { - class AutocompleteSupplier class AutocompleteGenerator - class AutocompleteItemSet - class PartitionedCommand + class AutocompleteSupplier + + class "AutocompleteItemSet" as AutocompleteItemSet + class "<>\nAutocompleteConstraint" as AutocompleteConstraint + class "<>\nFlagValueSupplier" as FlagValueSupplier - class "<>\nAutocompleteConstraint" as AutocompleteConstraint + class PartitionedCommand } package Model as ModelPackage <> { class "<>\nModel" as Model } -package "Parser classes" { +package "Parser classes" <> { class Flag } Class " " as HiddenOutside1 #FFFFFF -HiddenOutside1 .down.> AutocompleteGenerator - Class " " as HiddenOutside2 #FFFFFF +HiddenOutside1 .down.> AutocompleteGenerator HiddenOutside2 .right.> AutocompleteSupplier -AutocompleteGenerator .down.> "1" AutocompleteSupplier : uses > +AutocompleteGenerator -down-> "1" AutocompleteSupplier : uses > -AutocompleteSupplier ..> "1" AutocompleteItemSet -AutocompleteSupplier ..> "*" FlagValueSupplier +AutocompleteSupplier --> "1" AutocompleteItemSet +AutocompleteSupplier --> "*" FlagValueSupplier AutocompleteSupplier ..> Flag -AutocompleteItemSet ..> "*" AutocompleteConstraint : contains > +AutocompleteItemSet --> "*" AutocompleteConstraint : contains > AutocompleteGenerator ..> PartitionedCommand : creates > FlagValueSupplier .right.> PartitionedCommand : uses > FlagValueSupplier ..down.> Model : uses > -PartitionedCommand ..down.> Flag : depends on > +PartitionedCommand ..down.> Flag @enduml From 92dd84347ee61242ebc687ef66f67ef0ff8db6b9 Mon Sep 17 00:00:00 2001 From: Wern Date: Mon, 13 Nov 2023 19:09:08 +0800 Subject: [PATCH 03/23] Update DG details --- docs/DeveloperGuide.md | 81 +++++++++++++++++++++---- docs/diagrams/AutocompleteClasses.puml | 20 ++++-- docs/diagrams/LogicClassDiagram.puml | 6 +- docs/images/AutocompleteClasses.png | Bin 0 -> 39884 bytes docs/images/LogicClassDiagram.png | Bin 36640 -> 39899 bytes 5 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 docs/images/AutocompleteClasses.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 6003cb6ef0d..8a16200468a 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -100,23 +100,73 @@ The sequence diagram below illustrates the interactions within the `Logic` compo How the `Logic` component works: -1. When `Logic` is called upon to execute a command, it is passed to an `AppParser` object which in turn creates a parser that matches the command (e.g., `DeleteCommandParser`) and uses it to parse the command. -1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `DeleteCommand`) which is executed by the `LogicManager`. -1. The command can communicate with the `Model` when it is executed (e.g. to delete a contact). -1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. +* Executing a command: -Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command: + 1. When `Logic` is called upon to execute a command, it is passed to `AppParser` which in turn creates a parser that matches the command (e.g., `DeleteCommandParser`) and uses it to parse the command. + + 2. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `DeleteCommand`) which is executed by the `LogicManager`. + + 3. The command can communicate with the `Model` when it is executed (e.g. to delete a contact). + + 4. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. + +* Autocompleting a command: + + 1. When `Logic` is called upon to autocomplete a command, it is passed to `AppParser` which in turn creates an autocompletion generator capable of generate autocompletion results for this command. + + 2. This results in an `AutocompleteGenerator` which is executed by the `LogicManager`. + + 3. The `AutocompleteGenerator` can communicate with the `Model` to obtain the current application state (e.g. to obtain the list of all contact ids) when supplying autocompletion results. + + 4. This results in a `Stream` representing the possible completions, which is returned back from `Logic`. + +Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command for both execution and autocompletion: + +#### Parser classes How the parsing works: -* When called upon to parse a user command, the `AppParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AppParser` returns back as a `Command` object. -* All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. + +1. When called upon to parse a user command, the `AppParser` class looks up the corresponding **Command Parser** (e.g., `AddCommandParser` if it detects an "add" command). + +2. There are two cases here: + + 1. If there exists a `Parser` for the corresponding command, it will use the other classes shown above to parse the user command and create a `Command` object (e.g., `AddCommand`). + + 2. Otherwise, it will create a `Command` object corresponding to the command name (e.g., `AddCommand`) with no arguments. + +3. Finally, `AppParser` returns back the `Command` object. How arguments from a raw command input may be obtained by parsers: + * When arguments are needed for a command, `ArgumentTokenizer` is used to prepare and tokenize the raw input string, which can then convert it to an `ArgumentMultimap` for easy access. + * An `ArgumentMultimap` represents the command data (which has the format `name preamble text --flag1 value 1 --flag2 value 2`) in their distinct fields: **preamble**, **flags** and their mapped **values**. Note that as a multimap, multiple values can be mapped to the same flag. -* All parsers can use the `ArgumentMultimap` (obtained from using the raw input on `ArgumentTokenizer`) to access the required arguments to create and execute a `Command`. + +* With that, all parsers can use resulting `ArgumentMultimap` (obtained from using the raw input on `ArgumentTokenizer`) to access the required arguments to create and execute a `Command`. + +Design Notes: + +* All **Command Parser** classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. + +#### Autocomplete classes + + + +How autocompletion works: + +1. When called upon to generate autocompletions for a partially typed command, `Logic` passes the request to `AppParser` class. + +2. There are two cases after this happens: + + 1. If a command name is specified and complete (i.e., user added a space after the command name), `AppParser` will look up the corresponding `AutocompleteSupplier` for the command, and create an `AutocompleteGenerator` with it. + + 2. Otherwise, `AppParser` will create an `AutocompleteGenerator` with a `Supplier>` that returns all possible command names. + +3. `AppParser` then returns the `AutocompleteGenerator` to the requester so as they can generate autocompletion results. + +For full details of the autocomplete design and implementation, refer to the [Command Autocompletion Internals](#command-autocompletion-internals) section. ### Model component **API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) @@ -249,21 +299,30 @@ Jobby's Command Autocompletion is designed to provide users with intelligent com Just like programming IDEs, a user may type a prefix subsequence of a long command part, and simply press **TAB** to finish the command using the suggested match. For instance, type "sort -tt" and press **TAB** to finish the command as "sort --title". -The internal implementation consists of a few notable components: +#### The Autocomplete Package + +The full autocomplete package (and some of its dependencies) can be summarized by the following class diagram: + + + +The implementation consists of two notable classes that will be used by high-level users: - **`AutocompleteSupplier`**: - This class is responsible for generating possible flags and values to be used for suggestions. - It takes an `AutocompleteItemSet`, an optional `FlagValueSupplier` mapped to each flag, and can have corresponding `AutocompleteConstraint`s applied to flags. - It helps determine what flags can be added to an existing command phrase based on constraints and existing flags. + - All commands with customizable input should prepare an instance of this class to be used for autocompletion. - **`AutocompleteGenerator`**: - This component takes in an `AutocompleteSupplier` or a `Supplier>` and generates autocomplete results based on a partial command input and the current application model. - Users can invoke `AutocompleteGenerator#generateCompletions(command, model)` to get autocomplete suggestions. - It does the hard work of taking the possible values provided by either supplier, performing subsequence fuzzy match, and then "predict" what the user is typing. -For code looking to generation completion results from a command, the `AutocompleteGenerator` class is the only class that needs to be used, and any command that has autocomplete results will have a static constant of their respective `AutocompleteSupplier`. +The process used to generate autocomplete suggestions with these two classes is mentioned in the [high-level Logic component previously discussed](#logic-component). + +In summary, the `AutocompleteGenerator` class is used to generate completions, and any command that has the ability to supply autocomplete results should have their respective `AutocompleteSupplier`s defined and available to be fed into the generator. -#### Autocomplete Constraints +#### Autocompletion Constraints Autocompletion constraints are defined via the `AutocompleteConstraint` functional interface. It provides a way to specify rules for autocomplete suggestions. diff --git a/docs/diagrams/AutocompleteClasses.puml b/docs/diagrams/AutocompleteClasses.puml index 9e893f6beda..dfed450f38b 100644 --- a/docs/diagrams/AutocompleteClasses.puml +++ b/docs/diagrams/AutocompleteClasses.puml @@ -8,10 +8,22 @@ skinparam class { 'workaround to render generics properly without breaking the rest BorderColor LOGIC_COLOR BorderThickness 0 + StereotypeFontColor LOGIC_COLOR_T2 StereotypeFontSize 14 } +'hidden boxes +class " " as HiddenOutside1 <> +class " " as HiddenOutside2 <> +skinparam class { + BorderColor<> #FFFFFF + BackgroundColor<> #FFFFFF + StereotypeFontColor<> #FFFFFF + StereotypeFontSize<> 1 +} + +'packages package Autocomplete as AutocompletePackage <> { class AutocompleteGenerator class AutocompleteSupplier @@ -23,15 +35,13 @@ package Autocomplete as AutocompletePackage <> { class PartitionedCommand } -package Model as ModelPackage <> { - class "<>\nModel" as Model +package Model as ModelPackage { } package "Parser classes" <> { class Flag } -Class " " as HiddenOutside1 #FFFFFF -Class " " as HiddenOutside2 #FFFFFF +'relationships HiddenOutside1 .down.> AutocompleteGenerator HiddenOutside2 .right.> AutocompleteSupplier @@ -46,6 +56,6 @@ AutocompleteItemSet --> "*" AutocompleteConstraint : contains > AutocompleteGenerator ..> PartitionedCommand : creates > FlagValueSupplier .right.> PartitionedCommand : uses > -FlagValueSupplier ..down.> Model : uses > +FlagValueSupplier ..down.> ModelPackage : uses > PartitionedCommand ..down.> Flag @enduml diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index a57720890ee..920760fde92 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -6,7 +6,7 @@ skinparam classBackgroundColor LOGIC_COLOR package Logic as LogicPackage { -Class AddressBookParser +Class AppParser Class XYZCommand Class CommandResult Class "{abstract}\nCommand" as Command @@ -27,8 +27,8 @@ Class HiddenOutside #FFFFFF HiddenOutside ..> Logic LogicManager .right.|> Logic -LogicManager -right->"1" AddressBookParser -AddressBookParser ..> XYZCommand : creates > +LogicManager -right->"1" AppParser +AppParser ..> XYZCommand : creates > XYZCommand -up-|> Command LogicManager .left.> Command : executes > diff --git a/docs/images/AutocompleteClasses.png b/docs/images/AutocompleteClasses.png new file mode 100644 index 0000000000000000000000000000000000000000..28787acc40c482d1d014e755f214d5f8b02e893e GIT binary patch literal 39884 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 QFJ%A%Pgg&ebxsLQ0DZ4HT>t<8 literal 36640 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 From 0e88759d94ceedc13424668e2938ee836b031e5e Mon Sep 17 00:00:00 2001 From: Wern Date: Mon, 13 Nov 2023 20:46:22 +0800 Subject: [PATCH 04/23] Update DG and formatting consistency --- docs/DeveloperGuide.md | 55 +++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 8a16200468a..c61f68d6034 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -36,7 +36,7 @@ Given below is a quick overview of main components and how they interact with ea **Main components of the architecture** -**`Main`** (consisting of classes [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java)) is in charge of the app launch and shut down. +**`Main`** (consisting of classes [`Main`](https://github.com/AY2324S1-CS2103T-W08-3/tp/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/AY2324S1-CS2103T-W08-3/tp/tree/master/src/main/java/seedu/address/MainApp.java)) is in charge of the app launch and shut down. * At app launch, it initializes the other components in the correct sequence, and connects them up with each other. * At shut down, it shuts down the other components and invokes cleanup methods where necessary. @@ -68,13 +68,13 @@ The sections below give more details of each component. ### UI component -The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java) +**API Reference** : [`Ui.java`](https://github.com/AY2324S1-CS2103T-W08-3/tp/tree/master/src/main/java/seedu/address/ui/Ui.java) ![Structure of the UI Component](images/UiClassDiagram.png) The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. -The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml) +The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/AY2324S1-CS2103T-W08-3/tp/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/AY2324S1-CS2103T-W08-3/tp/tree/master/src/main/resources/view/MainWindow.fxml) The `UI` component, @@ -85,7 +85,7 @@ The `UI` component, ### Logic component -**API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java) +**API Reference** : [`Logic.java`](https://github.com/AY2324S1-CS2103T-W08-3/tp/tree/master/src/main/java/seedu/address/logic/Logic.java) Here's a (partial) class diagram of the `Logic` component: @@ -169,7 +169,7 @@ How autocompletion works: For full details of the autocomplete design and implementation, refer to the [Command Autocompletion Internals](#command-autocompletion-internals) section. ### Model component -**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) +**API Reference** : [`Model.java`](https://github.com/AY2324S1-CS2103T-W08-3/tp/tree/master/src/main/java/seedu/address/model/Model.java) @@ -190,7 +190,7 @@ The `Model` component, ### Storage component -**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java) +**API Reference** : [`Storage.java`](https://github.com/AY2324S1-CS2103T-W08-3/tp/tree/master/src/main/java/seedu/address/storage/Storage.java) @@ -426,23 +426,21 @@ Accessing the partial command is useful if you'd like to change the results base #### Partitioning Command Strings -The `PartitionedCommand` class is a simple wrapper for a command string that has been partitioned into its constituent parts, specifically for the purposes of autocomplete. +The `PartitionedCommand` class is a simple class for quick access for a command string's constituent parts, specifically for the purposes of autocomplete. This is done simply by initializing it with a partial command string. **API Reference:** [PartitionedCommand.java](https://github.com/AY2324S1-CS2103T-W08-3/tp/tree/master/src/main/java/seedu/address/logic/autocomplete/components/PartitionedCommand.java) -Most notably, it is capable of separating a command into these fundamental parts: +For example, given the partial command "`add --org --name Alice --oid ama`", you will be able to extract the partitions in the following forms: -``` -[command name] [middle, untouched text] [autocompletable text] -``` +| Command Name | Middle Text | Autocompletable Text | +|:------------:|:--------------------------:|:--------------------:| +| `add` | `--org --name Alice --oid` | `ama` | -For example, given the partial command "`add --org --name Alice --oid ama`", the partitioned command would be: +| Leading Text | Trailing Text | +|:------------------------------:|:-------------:| +| `add --org --name Alice --oid` | `ama` | -``` -[add] [--rec --name Alice --oid ] [ama] -``` - -It also provides other quick access properties which can be found in the API reference. +There are also helper methods to detect flag strings and other properties of the command, which can be found in the API reference. #### The Autocomplete Supplier @@ -490,25 +488,38 @@ Once initialized, users can simply call the `#generateCompletions(command, model **API Reference:** [AutocompleteGenerator.java](https://github.com/ay2324s1-cs2103t-w08-3/tp/tree/master/src/main/java/seedu/address/logic/autocomplete/AutocompleteGenerator.java) -##### High-level Internal Implementation +##### Execution Flow Internally, whenever requested, the `AutocompleteGenerator`: 1. obtains a command's parts with `PartitionedCommand`, -2. uses the given supplier to obtain the results based on the available parts, +2. uses the `AutocompleteSupplier` provided when initialized to obtain the results based on the available parts, 3. automatically performs fuzzy (subsequence) matching to filter results, 4. ranks them based on their relevance, 5. and finally returns a stream of autocompleted commands. -#### Design Considerations +### Design Considerations When designing the Autocomplete feature, important considerations include the ability to flexibly define and craft new constraints based on heuristically determined rules. -By abstracting away all operations into simple components like sets and constraints, the current carefully crafted design allows -Jobby's Command Autocompletion to provide context-aware suggestions to users, while adhering to simple constraints defined on a per command basis. +By abstracting away all operations into simple components like sets and constraints, the current carefully crafted design allows Jobby's Command Autocompletion to provide context-aware suggestions to users, while adhering to simple constraints defined on a per command basis. Most notably, it also allows for advanced rulesets to be specified in a human-readable fashion. Take a look at [AddCommand#AUTOCOMPLETE_SUPPLIER](https://github.com/AY2324S1-CS2103T-W08-3/tp/blob/c484696fe4c12d514ad3fb6a71ff2dfea089fe32/src/main/java/seedu/address/logic/commands/AddCommand.java#L47). +#### Alternatives Considered + +##### Alternative 1: Using Hardcoded Rules in Java + +One obvious alternative is to simply the possible autocompletion results for each command in standard Java. We may achieve this by manually checking against a command string for each command type, and using existing tokenization classes like `ArgumentTokenizer` and `ArgumentMultimap`. + +While this would incur less overhead in initial development time, more explicit coding is required - it is neither quick to write nor scalable to tons of commands. This is especially important as autocomplete was developed in parallel with other new features being added to Jobby, which would require constant changes to the autocomplete rules. + +##### Alternative 2: Using a Graph-based Approach + +A graph based approach, e.g., having a tree structure to define constraints and dependencies, may be more efficient than the current solution (which has to check against _all_ known rules every single time). + +However, it will consume even more development time to implement and model the rules as a graph. Since the current implementation involves one set with a list of constraints, multiple sets can be combined by simply concatenation of both the items and the constraints. + ### \[Proposed\] Undo/redo feature #### Proposed Implementation From 9b4cd9f7fe290cfd4232714e41b3883c6da24ff9 Mon Sep 17 00:00:00 2001 From: Wern Date: Mon, 13 Nov 2023 21:12:06 +0800 Subject: [PATCH 05/23] Update PPP --- docs/team/wxwern.md | 77 ++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 46 deletions(-) diff --git a/docs/team/wxwern.md b/docs/team/wxwern.md index 3b06e43a731..8b37324cda6 100644 --- a/docs/team/wxwern.md +++ b/docs/team/wxwern.md @@ -7,73 +7,62 @@ title: Wern's Project Portfolio Page ### Project: Jobby -Jobby is a desktop application used for tracking job applications. +Jobby is a desktop application used for managing your job applications and associated organization and recruiter contacts. Given below are my contributions to the project. -* **New Feature**: - - * **Command Autocompletion** +* **New Feature**: Command Autocompletion * This allows users to autocomplete their commands, just like command terminals or programming IDEs, such as by pressing **TAB**. * Like an IDE, it does a _subsequence_ match, so typing `-dsp` then **TAB** can select `--description`, allowing for fast disambiguation from another similar term like `--descending`. - * As a bonus, it also supports undoing with **BACKSPACE**. It keeps a complete undo history, so you can do multiple undos for the same command as long if you did not move your text cursor from the end point. - - * **Rationale:** - - * When developing Jobby, we started having too many command parameters with clashing prefixes, so it no longer make sense to offer single letter parameter names. - - * Memorizing multi-letter abbreviations for a bunch of parameter is more difficult than a full name. + * As a bonus, it also supports undoing with **BACKSPACE**. It keeps a complete undo history, so you can invoke multiple undos with repeated **BACKSPACE** keystrokes if you only edited text on the trailing (right) side of the command input. - * However, a full parameter name takes much longer to type, which means it's no longer optimized for fast typists. - - * Hence, autocompletion was implemented to resolve all of these concerns. + * **Rationale:** It allows for faster command input, and reduces the need to memorize the exact command syntax. * **Implementation:** The internal implementation is written completely from scratch. This allows it to: * specifically parse our custom command structure, and - * dynamically determine what should or should not be suggested next based on a given set of rules, or available data in the app. * **UI:** The autocompletion UI is adapted from [@floralvikings's AutoCompleteTextBox.java](https://gist.github.com/floralvikings/10290131) with significant enhancements. These include: - * overhauling the implementation in modern Java syntax and JavaFX method calls, + * overhauling the implementation in modern Java syntax and JavaFX method calls, and support for our custom `Stream`-based autocompletion result suppliers, - * modified the implementation idea to support for our custom `Stream`-based autocompletion result suppliers, + * add indicators for invoking the best (first-in-list) autocomplete result, and prominently highlighting _only_ the suggested postfix term while graying out the rest, - * integrating it directly within FXML, + * added method calls for external parties to invoke autocompletion and undo autocompleted results (e.g., via preferred key events or UI buttons). - * improved the UI to support adding indicators for invoking the best (first-in-list) autocomplete result, - - * added support for auto-highlighting only the postfix term that is suggested for improved glancability, +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=AY2324S1-CS2103T-W08-3&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-09-22&tabOpen=true&tabType=authorship&tabAuthor=wxwern&tabRepo=AY2324S1-CS2103T-W08-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~other~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) - * added support for external parties to invoke autocompletion (e.g., via preferred key events or UI buttons), and +* **Project management**: - * added support for undoing autocompleted results (and similarly for external parties to invoke undo). + * Configuring the Issue Tracker Tags and Milestones for the project. + * Setting up GitHub Actions for the project, including Java CI, CodeCov and GitHub Pages. + * Styling the website for optimal readability and print formatting, including: -* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=AY2324S1-CS2103T-W08-3&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-09-22&tabOpen=true&tabType=authorship&tabAuthor=wxwern&tabRepo=AY2324S1-CS2103T-W08-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~other~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) + * Styling headers with improved spacing, typography and color for increased readability. -* **Project management**: + * Tweaking the page-break rules for different elements, such as preventing page breaks on crucial boxes or enforcing page breaks immediately after certain headers. - * To be added... + * Styling custom unified UG elements, like the following: + * :trophy: How to perform a task :information_source: An info message pill + * :warning: A warning message pill :warning: A danger message pill + * Beginner Intermediate Expert + * Organization Recruiter Job Application * **Enhancements to existing features**: * Revamped the parameter syntax to use a prefix of `--param`. - * This allows for improved autocompletion UX as compared to `param/`, since we can immediately determine if the user intends to type it based on the first character. - + * This allows for improved autocompletion UX as compared to `param/`, since we can immediately determine if the user intends to type a parameter based on the first character. * It is also much less likely to clash with an existing user input. * Swapped out random ID generation with an implementation to derive IDs from an input name. * This allows for improved UX when editing details that require an ID, combined with autocomplete integration. - - * Previously, it was implemented by another team member by generating random RFC 4122 UUIDs instead. - - * Now, random IDs are derived from the name, e.g., `google-sg-a8b3fe` from an input of `Google SG`. + * e.g., `google-sg-a8b3fe` can be derived from an input of `Google SG`. * **Documentation**: @@ -85,25 +74,21 @@ Given below are my contributions to the project. * Added usage guides for command autocompletion. - * Styling the website for improved overall glancability and support for fully-automated print formatting, including: - - * Styling headers with improved spacing, typography and color for increased readability. - - * Tweaking the page-break rules for different elements, such as preventing page breaks on crucial boxes or enforcing page breaks immediately after certain headers. - - * Styling custom unified UG elements for readability, like the following: - * :trophy: How to perform a task :information_source: An info message pill - * :warning: A warning message pill :warning: A danger message pill - * Beginner Intermediate Expert - * Organization Recruiter Job Application + * Styling the website for improved overall readability and automated print formatting. * Developer Guide: - * Added a quick explanation of how "Parser classes" may obtain the properties of our custom `Flag`s (like `--name`) from a command via `ArgumentMultimap` and `ArgumentTokenizer`. + * Integrated explanations of how "Autocomplete classes" work in the context of the `Logic` package. + + * Updated how `AppParser` (formerly `AddressBookParser`) operates in the context of our app, since we now dynamically look up details and also support autocompletion. + + * Added a complete high-level explanation of Jobby's internal autocomplete implementation and interactions. - * Added a high-level end-to-end explanation of Jobby's internal autocomplete implementation and interactions. + * Added use cases for autocompletion. * **Community**: - * To be added... + * Detailed PR Reviews (e.g., ) + * Forum contributions (e.g., ) + * Reported bugs and suggestions for other teams (e.g., during PE-D) From 30056f640453b74c4f62c2bfc8395b11bd82a871 Mon Sep 17 00:00:00 2001 From: Wern Date: Mon, 13 Nov 2023 21:27:41 +0800 Subject: [PATCH 06/23] Update DG autocompletion effort --- docs/DeveloperGuide.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 0fbdb23c4b2..73c3bbcbe63 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1208,11 +1208,17 @@ Additionally, a huge amount of time was spent creating test cases and modifying ### Autocompletion -To improve user experience, we wanted to incorporate an autocomplete feature which allows users to know which flags could be used for a certain command. This reduced the reliance on the User Guide and would help new users familiarize themselves with Jobby. +To improve user experience, we wanted to incorporate support for command autocompletion, which allows users to know which parameters and values could be used for a certain command. This reduced the reliance on the User Guide and would help new users familiarize themselves with Jobby. + +Additionally, the addition of autocompletion allows us to use full-length flags (e.g., `--description`), yet allowing the user to simply type `-dc ` to obtain the full flag, removing the need to memorize multiple 1-, 2-letter abbreviations. This involved: -* Creating an entire class to encapsulate this feature. -* Modify the parser to aid in autocompletion. -* Modifying JavaFX to incorporate autocompletion. +* Creating multiple classes to better organize and provide autocompletion, +* Modifying the parser, tokenizer, flag syntax, etc., to aid in autocompletion, and +* Modifying JavaFX elements and intercepting the appropriate keystroke events to incorporate autocompletion. + +The end-to-end process of parsing the user text and determining what are plausible inputs is not trivial - we need to correctly decide which parameters and values are suggestable values based on command rules, such as "only one of `--name` allowed". + +Additionally, despite the complex implementation, the autocompletion package has a comprehensive ~80% test coverage, with expected behaviors clearly documented within the tests themselves to guard against unexpected changes. -This was extremely challenging as including a proper autocompletion in JavaFX was not simple and straightforward. +For more details, refer to the [Command Autocompletion Internals](#command-autocompletion-internals). From 4e64e946c92c74d1951be94c0bc1d40f50527564 Mon Sep 17 00:00:00 2001 From: Wern Date: Mon, 13 Nov 2023 21:45:43 +0800 Subject: [PATCH 07/23] Add acknowledgements --- docs/DeveloperGuide.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 73c3bbcbe63..412cfe48dd0 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -9,7 +9,12 @@ title: Developer Guide ## **Acknowledgements** -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +* UI rendering via: [JavaFX](https://openjfx.io/) +* Testing suite via: [JUnit5](https://github.com/junit-team/junit5) +* JSON data saving and loading via: [Jackson](https://github.com/FasterXML/jackson) + +* Jobby base UI adapted from: [AddressBook Level-3](https://se-education.org/addressbook-level3/) +* Autocompletion base UI adapted from: [@floralvikings's AutoCompleteTextBox.java](https://gist.github.com/floralvikings/10290131) -------------------------------------------------------------------------------------------------------------------- From 29c8b87284b12cacc6c6fa531a8a3b2cd0c05c4f Mon Sep 17 00:00:00 2001 From: Wern Date: Mon, 13 Nov 2023 21:46:22 +0800 Subject: [PATCH 08/23] Improve PPP --- docs/team/wxwern.md | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/docs/team/wxwern.md b/docs/team/wxwern.md index 8b37324cda6..31fa8e7ac2d 100644 --- a/docs/team/wxwern.md +++ b/docs/team/wxwern.md @@ -13,26 +13,15 @@ Given below are my contributions to the project. * **New Feature**: Command Autocompletion - * This allows users to autocomplete their commands, just like command terminals or programming IDEs, such as by pressing **TAB**. + * This allows users to autocomplete their commands, just like command terminals or programming IDEs, such as by pressing **TAB**, and even undo when you **BACKSPACE**. * Like an IDE, it does a _subsequence_ match, so typing `-dsp` then **TAB** can select `--description`, allowing for fast disambiguation from another similar term like `--descending`. - * As a bonus, it also supports undoing with **BACKSPACE**. It keeps a complete undo history, so you can invoke multiple undos with repeated **BACKSPACE** keystrokes if you only edited text on the trailing (right) side of the command input. + * **Rationale:** It allows for faster command input, and reduces command syntax memorization. - * **Rationale:** It allows for faster command input, and reduces the need to memorize the exact command syntax. + * **Implementation:** The internal implementation is written completely from scratch to support our custom formats - more information available in the Developer Guide. - * **Implementation:** The internal implementation is written completely from scratch. This allows it to: - - * specifically parse our custom command structure, and - * dynamically determine what should or should not be suggested next based on a given set of rules, or available data in the app. - - * **UI:** The autocompletion UI is adapted from [@floralvikings's AutoCompleteTextBox.java](https://gist.github.com/floralvikings/10290131) with significant enhancements. These include: - - * overhauling the implementation in modern Java syntax and JavaFX method calls, and support for our custom `Stream`-based autocompletion result suppliers, - - * add indicators for invoking the best (first-in-list) autocomplete result, and prominently highlighting _only_ the suggested postfix term while graying out the rest, - - * added method calls for external parties to invoke autocompletion and undo autocompleted results (e.g., via preferred key events or UI buttons). + * **UI:** The autocompletion UI is adapted from [@floralvikings's AutoCompleteTextBox.java](https://gist.github.com/floralvikings/10290131). The reference text box only has a barebones context menu, but significant enhancements has been made to the styling and behavior to improve readability and UX (like partial term highlighting and undo support), which can be seen when using Jobby. * **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=AY2324S1-CS2103T-W08-3&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-09-22&tabOpen=true&tabType=authorship&tabAuthor=wxwern&tabRepo=AY2324S1-CS2103T-W08-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~other~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) @@ -47,39 +36,38 @@ Given below are my contributions to the project. * Tweaking the page-break rules for different elements, such as preventing page breaks on crucial boxes or enforcing page breaks immediately after certain headers. * Styling custom unified UG elements, like the following: - * :trophy: How to perform a task :information_source: An info message pill - * :warning: A warning message pill :warning: A danger message pill - * Beginner Intermediate Expert + * :trophy: How to perform a task :information_source: An info pill :warning: A warning pill :warning: A danger pill * Organization Recruiter Job Application + * Beginner Intermediate Expert

* **Enhancements to existing features**: * Revamped the parameter syntax to use a prefix of `--param`. * This allows for improved autocompletion UX as compared to `param/`, since we can immediately determine if the user intends to type a parameter based on the first character. + * It is also much less likely to clash with an existing user input. * Swapped out random ID generation with an implementation to derive IDs from an input name. * This allows for improved UX when editing details that require an ID, combined with autocomplete integration. + * e.g., `google-sg-a8b3fe` can be derived from an input of `Google SG`. * **Documentation**: * User Guide: - * Added a simpler command syntax introduction. - - * Improved explanation of command symbols used in the guide. + * Added a structured command syntax introduction, and instructions to interpret command formats in the UG and app. * Added usage guides for command autocompletion. - * Styling the website for improved overall readability and automated print formatting. + * Styling the website for improved overall readability and automated print formatting (see above in Project Management). * Developer Guide: * Integrated explanations of how "Autocomplete classes" work in the context of the `Logic` package. - + * Updated how `AppParser` (formerly `AddressBookParser`) operates in the context of our app, since we now dynamically look up details and also support autocompletion. * Added a complete high-level explanation of Jobby's internal autocomplete implementation and interactions. From 020ec7a51231ea726dd50ae35ae58676cc13bd75 Mon Sep 17 00:00:00 2001 From: Wern Date: Mon, 13 Nov 2023 22:14:09 +0800 Subject: [PATCH 09/23] Update pill style --- docs/_sass/minima/custom-styles.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_sass/minima/custom-styles.scss b/docs/_sass/minima/custom-styles.scss index 43f754305e7..79687640222 100644 --- a/docs/_sass/minima/custom-styles.scss +++ b/docs/_sass/minima/custom-styles.scss @@ -83,6 +83,8 @@ img.emoji { font-size: 75%; vertical-align: text-bottom; + display: inline-block; + .pill { /* so nesting pills will look the same */ font-size: 100%; @@ -139,10 +141,8 @@ img.emoji { border-color: #e8e6e5; color: #444444; - padding: 4px 12px; padding-right: 2px; margin: 4px 2px; - display: inline-block; &.jobby-data-class:not(:first-child) { margin-left: 0; From 8649e947075e523f29369eff15ae87385f6e476e Mon Sep 17 00:00:00 2001 From: Wern Date: Mon, 13 Nov 2023 22:29:11 +0800 Subject: [PATCH 10/23] Fix formatting --- docs/DeveloperGuide.md | 4 +++- docs/UserGuide.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 412cfe48dd0..4502707acb7 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -19,6 +19,7 @@ title: Developer Guide -------------------------------------------------------------------------------------------------------------------- ## **Setting up, getting started** +{: .reset-page-break-defaults} Refer to the guide [_Setting up and getting started_](SettingUp.md). @@ -26,9 +27,10 @@ Refer to the guide [_Setting up and getting started_](SettingUp.md). ## **Design** -
+
:bulb: **Tip:** The `.puml` files used to create diagrams in this document `docs/diagrams` folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams. +
### Architecture diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 98a543d933e..c03d2af2f17 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -74,7 +74,7 @@ title: User Guide ### Small information pills -| Component | Description | +| Component | Description | |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | :trophy: Learning outcomes | The learning outcome of the section. | | Beginner
Intermediate
Expert | The difficulty level of the section, with Beginner for new users, Intermediate for users who have completed the tutorial, and Expert for users who have completed and used the features in the User Guide. | From 78e51e5becfbef5dbd8287dfcff6920fa8ea9928 Mon Sep 17 00:00:00 2001 From: Wern Date: Mon, 13 Nov 2023 23:01:26 +0800 Subject: [PATCH 11/23] Fix PPP --- docs/team/wxwern.md | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/docs/team/wxwern.md b/docs/team/wxwern.md index 31fa8e7ac2d..24a0d84732a 100644 --- a/docs/team/wxwern.md +++ b/docs/team/wxwern.md @@ -11,7 +11,7 @@ Jobby is a desktop application used for managing your job applications and assoc Given below are my contributions to the project. -* **New Feature**: Command Autocompletion +* **New Feature**: Command Autocompletion ([PR #63](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/63)) * This allows users to autocomplete their commands, just like command terminals or programming IDEs, such as by pressing **TAB**, and even undo when you **BACKSPACE**. @@ -28,7 +28,8 @@ Given below are my contributions to the project. * **Project management**: * Configuring the Issue Tracker Tags and Milestones for the project. - * Setting up GitHub Actions for the project, including Java CI, CodeCov and GitHub Pages. + * Setting up GitHub Actions for the project, including Java CI, CodeCov, GitHub Pages, PR Checks. + * Managed releases [v1.2 and v1.3](https://github.com/AY2324S1-CS2103T-W08-3/tp/releases). * Styling the website for optimal readability and print formatting, including: * Styling headers with improved spacing, typography and color for increased readability. @@ -43,40 +44,27 @@ Given below are my contributions to the project. * **Enhancements to existing features**: * Revamped the parameter syntax to use a prefix of `--param`. - * This allows for improved autocompletion UX as compared to `param/`, since we can immediately determine if the user intends to type a parameter based on the first character. - - * It is also much less likely to clash with an existing user input. - + * It is also much less likely to clash with an existing user input.

* Swapped out random ID generation with an implementation to derive IDs from an input name. - * This allows for improved UX when editing details that require an ID, combined with autocomplete integration. - - * e.g., `google-sg-a8b3fe` can be derived from an input of `Google SG`. + * e.g., `google-sg-a8b3fe` can be derived from an input of `Google SG`.

* **Documentation**: * User Guide: - - * Added a structured command syntax introduction, and instructions to interpret command formats in the UG and app. - + * Added a structured command syntax introduction, and instructions to interpret command formats. * Added usage guides for command autocompletion. - - * Styling the website for improved overall readability and automated print formatting (see above in Project Management). - + * Styling the website for improved overall readability and automated print formatting (see above in Project Management).

* Developer Guide: - * Integrated explanations of how "Autocomplete classes" work in the context of the `Logic` package. - * Updated how `AppParser` (formerly `AddressBookParser`) operates in the context of our app, since we now dynamically look up details and also support autocompletion. - - * Added a complete high-level explanation of Jobby's internal autocomplete implementation and interactions. - - * Added use cases for autocompletion. + * Added a complete high-level explanation of Jobby's internal autocomplete implementation. + * Added use cases for autocompletion.

* **Community**: - * Detailed PR Reviews (e.g., ) - * Forum contributions (e.g., ) + * Detailed PR Reviews (e.g., [#32](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/32), [#34](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/34), [#39](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/39), [#69](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/69), [#183](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/183)) + * Forum contributions (e.g., [#30](https://github.com/nus-cs2103-AY2324S1/forum/issues/30), [#103](https://github.com/nus-cs2103-AY2324S1/forum/issues/103)) * Reported bugs and suggestions for other teams (e.g., during PE-D)
From 51b332a1fdd0afbb02cbdd56130569625f72dc86 Mon Sep 17 00:00:00 2001 From: Wern Date: Mon, 13 Nov 2023 23:08:20 +0800 Subject: [PATCH 12/23] Add UG structure credits Co-authored-by: tanshiyu --- docs/DeveloperGuide.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 625df7a86b1..5d640e3191d 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -16,6 +16,8 @@ title: Developer Guide * Jobby base UI adapted from: [AddressBook Level-3](https://se-education.org/addressbook-level3/) * Autocompletion base UI adapted from: [@floralvikings's AutoCompleteTextBox.java](https://gist.github.com/floralvikings/10290131) +* New user tutorial structure inspired from: [AY2324S1-CS2103T-T17-03](https://ay2324s1-cs2103t-t17-3.github.io/tp/UserGuide.html) + -------------------------------------------------------------------------------------------------------------------- ## **Setting up, getting started** From 03fe3a56bfeef360adc9e89cda08e5824d76ce3b Mon Sep 17 00:00:00 2001 From: Wern Date: Mon, 13 Nov 2023 23:13:00 +0800 Subject: [PATCH 13/23] Update index.md --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 6cc225a49e5..16bbf079408 100644 --- a/docs/index.md +++ b/docs/index.md @@ -20,4 +20,4 @@ title: Jobby **Acknowledgements** -* Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5) +Refer to the [Developer Guide's Acknowledgements section](DeveloperGuide.html#acknowledgements). From fcb20f85d63e0d1e58f76d6b41184b81910892e9 Mon Sep 17 00:00:00 2001 From: Wern Date: Mon, 13 Nov 2023 23:42:25 +0800 Subject: [PATCH 14/23] Add syntax highlighting to all flag code blocks --- docs/UserGuide.md | 220 +++++++++++++++++++++++----------------------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index cddd82a3706..aaefb8e2e33 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -70,7 +70,7 @@ title: User Guide ### Code blocks for entering commands -   denotes a command or a part of a command that can be entered in Jobby. For example, `add --org --name Woogle` is a command. `add` is also part of a command. +   denotes a command or a part of a command that can be entered in Jobby. For example, `add --org --name Woogle`{:.language-sh} is a command. `add` is also part of a command. ### Small information pills @@ -150,8 +150,8 @@ Solid! Now it's time to get started with Jobby! Let's say you are interested to applying to **Google** as your internship destination, and you found their email **google@gmail.com**. (This is not their real email, of course) -You can use [`add --org`](#adding-organizations---add---org) command here to add Google into your organization: -1. Type `add --org --name Google --id google_id --email google@gmail.com` into the command box +You can use [`add --org`{:.language-sh}](#adding-organizations---add---org) command here to add Google into your organization: +1. Type `add --org --name Google --id google_id --email google@gmail.com`{:.language-sh} into the command box 2. Press **ENTER** ![Adding Organization](images/ug-images/org-added.png) @@ -163,8 +163,8 @@ You have successfully added **Google**, with the email **google@gmail.com** into In a job fair, you managed to meet a **Google** internship recruiter, **Josh Mao**, and he provided you with his number **91219121**. -Here is how you can use [`add --rec`](#adding-recruiters---add---rec) command to track the Recruiter data in Jobby: -1. Type `add --rec --name Josh Mao --oid google_id --phone 91219121` into the command box +Here is how you can use [`add --rec`{:.language-sh}](#adding-recruiters---add---rec) command to track the Recruiter data in Jobby: +1. Type `add --rec --name Josh Mao --oid google_id --phone 91219121`{:.language-sh} into the command box 2. Press **ENTER** ![Adding Recruiter](images/ug-images/rec-added.png) @@ -176,7 +176,7 @@ You have successfully added **Josh Mao**, who is a **recruiter** from Google, wh After preparing your resume, you are ready to apply to **Google** as an intern for their **Software Engineer** role! And you know that the application deadline is on the **22-11-2023**. Here is how you can use [`apply`](#applying-to-organizations---apply) command to track your application in Jobby: -1. Type `apply google_id --title Software Engineer --by 22-11-2023` into the command box +1. Type `apply google_id --title Software Engineer --by 22-11-2023`{:.language-sh} into the command box 2. Press **ENTER** ![Adding Application](images/ug-images/app-added.png) @@ -203,15 +203,15 @@ In Jobby, we write commands in the command box at the top of Jobby's window. Commands are made up of a few parts: The **command**, **parameter names** and **input values**. -A command like "`edit google --name Google SG --id google-sg`" would refer to: +A command like "`edit google --name Google SG --id google-sg`{:.language-sh}" would refer to: * the **command** `edit`, * with a **command value** `google`, -* with a **parameter** `--name`, +* with a **parameter** `--name`{:.language-sh}, * which has the **parameter value** `Google SG`, -* with a **parameter** `--id`, +* with a **parameter** `--id`{:.language-sh}, * which has the **parameter value** `google-sg`. -Parameters may be in any order, whose names are of the form `-a` or `--abc123`, and must be surrounded by whitespace. +Parameters may be in any order, whose names are of the form `-a` or `--abc123`{:.language-sh}, and must be surrounded by whitespace. Any extra parameters and values to commands that don't accept them will either be ignored or throw an error. @@ -237,7 +237,7 @@ Throughout this guide, you will find symbols and placeholders used to describe a * The parts where you should be typing your parameter values. - * e.g., `--name NAME` means inputting names along the lines of `--name Alice`. + * e.g., `--name NAME`{:.language-sh} means inputting names along the lines of `--name Alice`{:.language-sh}. * **Terms separated by `/` or `|`** @@ -245,19 +245,19 @@ Throughout this guide, you will find symbols and placeholders used to describe a * These may be included in the parameter names or value description. - * e.g., `--a/--b` means either `--a` or `--b` but not `--a --b`. + * e.g., `--a/--b`{:.language-sh} means either `--a`{:.language-sh} or `--b`{:.language-sh} but not `--a --b`{:.language-sh}. * **Terms surrounded by `[` and `]`** * An optional parameter or option that may be omitted. - * e.g., `[--id ID]` means you may omit setting an ID for the command. + * e.g., `[--id ID]`{:.language-sh} means you may omit setting an ID for the command. * **Terms ending with `...`** * The parameter is multivalued. - * e.g., `[--tag TAG]...` means `--tag` and its value can be repeated from 0 to any number of times. + * e.g., `[--tag TAG]...`{:.language-sh} means `--tag`{:.language-sh} and its value can be repeated from 0 to any number of times. @@ -288,10 +288,10 @@ If suggestions were hidden or aren't shown when they should, press **TAB** to pr * Expert Autocomplete checks for fuzzy matches - it sorts by the best *subsequence* prefix match first. - * For example, you can type `-nm` to get the autocompletion result of `--name`. + * For example, you can type `-nm` to get the autocompletion result of `--name`{:.language-sh}. * This allows you to quickly choose between parameter names with similar prefixes, e.g., by typing - `-dsp` to select `--description` instead of `--descending`. + `-dsp` to select `--description`{:.language-sh} instead of `--descending`{:.language-sh}. @@ -314,7 +314,7 @@ If suggestions were hidden or aren't shown when they should, press **TAB** to pr The `add` command allows you to create contacts to track details about the organizations and recruiters related to your job application process. To learn more about creating each type of contact, check out the sections below. -#### Adding organizations - `add --org` +#### Adding organizations - `add --org`{:.language-sh} :trophy: How to add organization contacts into Jobby Beginner @@ -331,20 +331,20 @@ Adds an organization contact with the details given to the command. | Command | Reason | |------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------| -| `add --org --name J&J` | Adding an organization **J&J**. | -| `add --org --name Google --id g-sg --phone 98765432 ` | Adding an organization **Google** with other flags. | -| `add --org --name Examinations NUS --phone 65166269 --email examinations@nus.edu.sg --url https://luminus.nus.edu.sg/` | Adding an organization **Examination NUS** with other flags. | +| `add --org --name J&J`{:.language-sh} | Adding an organization **J&J**. | +| `add --org --name Google --id g-sg --phone 98765432 `{:.language-sh} | Adding an organization **Google** with other flags. | +| `add --org --name Examinations NUS --phone 65166269 --email examinations@nus.edu.sg --url https://luminus.nus.edu.sg/`{:.language-sh} | Adding an organization **Examination NUS** with other flags. | ##### Invalid examples | Command | Reason | |-------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------| -| `add --org --name` | `--name` field used but not specified. | -| `add --org --name Google --phone 1231*&&@` | Optional field (in this case `--phone`) was not following the [accepted parameters](#appendix-a-acceptable-values-for-parameters). | +| `add --org --name`{:.language-sh} | `--name`{:.language-sh} field used but not specified. | +| `add --org --name Google --phone 1231*&&@`{:.language-sh} | Optional field (in this case `--phone`{:.language-sh}) was not following the [accepted parameters](#appendix-a-acceptable-values-for-parameters). | -#### Adding recruiters - `add --rec` +#### Adding recruiters - `add --rec`{:.language-sh} :trophy: How to add recruiter contacts into Jobby Beginner @@ -355,11 +355,11 @@ add --rec --name NAME [-id ID] [--oid ORG_ID] [--phone NUMBER] [--email EMAIL] [ Adds a recruiter contact with the details given to the command. * If an `ID` is not specified, one will be automatically generated. -* To link a Recruiter to an Organization in the contacts list, make sure you include `--oid` and pass in the `ID` of the Organization you want to link to. +* To link a Recruiter to an Organization in the contacts list, make sure you include `--oid`{:.language-sh} and pass in the `ID` of the Organization you want to link to. * If you wish to know more about the requirements for each parameter, check out the [given appendix](#appendix-a-acceptable-values-for-parameters). ##### Sample demonstration -* If you execute the command: `add --rec --name Ryan Koh --oid job_seeker_plus`, you should see a new Recruiter being added to the bottom of the contacts list. +* If you execute the command: `add --rec --name Ryan Koh --oid job_seeker_plus`{:.language-sh}, you should see a new Recruiter being added to the bottom of the contacts list. * The newly added contact will have a special label _from organization (job\_seeker\_plus)_ to indicate that the Recruiter is associated to the Organization with that particular `ID`. @@ -371,18 +371,18 @@ Adds a recruiter contact with the details given to the command. | Command | Reason | |-------|----------| -| `add --rec --name John Doe` | Adds a recruiter that is not associated to any organization. | -| `add --rec --name John Doe --tag friendly --tag woogle` | Adds a recruiter with two tags - friendly and woogle. | -| `add --rec --name John Doe --oid job_seeker_plus` | Adds a recruiter that is associated to an organization (if it exists in the address book) with the id **job_seeker_plus**. | -| `add --rec --name John Doe --id johndoe_123 --oid job_seeker_plus --number 912832192 --email johndoe@nus.edu.sg --url example.com --address 21 Kent Ridge Rd --tag network` | Adds a recruiter with all the possible fields. | +| `add --rec --name John Doe`{:.language-sh} | Adds a recruiter that is not associated to any organization. | +| `add --rec --name John Doe --tag friendly --tag woogle`{:.language-sh} | Adds a recruiter with two tags - friendly and woogle. | +| `add --rec --name John Doe --oid job_seeker_plus`{:.language-sh} | Adds a recruiter that is associated to an organization (if it exists in the address book) with the id **job_seeker_plus**. | +| `add --rec --name John Doe --id johndoe_123 --oid job_seeker_plus --number 912832192 --email johndoe@nus.edu.sg --url example.com --address 21 Kent Ridge Rd --tag network`{:.language-sh} | Adds a recruiter with all the possible fields. | ##### Invalid examples | Command | Reason | |---------|--------| -| `add --rec` | Missing a name. | -| `add --rec --name John Doe --phone` | Optional fields (in this case `--phone`) were used but not specified. | -| `add --rec --name John Doe --oid bogus-org` | Given that no organization with the id "bogus-org" exists in the address book. | +| `add --rec`{:.language-sh} | Missing a name. | +| `add --rec --name John Doe --phone`{:.language-sh} | Optional fields (in this case `--phone`{:.language-sh}) were used but not specified. | +| `add --rec --name John Doe --oid bogus-org`{:.language-sh} | Given that no organization with the id "bogus-org" exists in the address book. | ### Editing contacts - `edit` @@ -402,15 +402,15 @@ Edits the given contact according to the parameters given. | Command | Reason | |-----------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------| -| `edit google --phone 91292951` | Change phone number of organization with **ID** google to **91292951**. | -| `edit 1 --name Jane Street` | Change name of contact at index 1 to **Jane Street**. | -| `edit 1 --name Google --phone 91241412 --email google@gmail.sg` | Changes the name, phone number and email of the contact at index 1 to `Google`, `91241412` and `google@gmail.sg` respectively. | +| `edit google --phone 91292951`{:.language-sh} | Change phone number of organization with **ID** google to **91292951**. | +| `edit 1 --name Jane Street`{:.language-sh} | Change name of contact at index 1 to **Jane Street**. | +| `edit 1 --name Google --phone 91241412 --email google@gmail.sg`{:.language-sh} | Changes the name, phone number and email of the contact at index 1 to `Google`, `91241412` and `google@gmail.sg` respectively. | ##### Invalid examples | Command | Reason | |-----------------------------------------|-------------------------------------------------------------------------------------| -| `edit google --phone 8124!@#$` | `--phone` has an [invalid parameter](#appendix-a-acceptable-values-for-parameters) | +| `edit google --phone 8124!@#$`{:.language-sh} | `--phone`{:.language-sh} has an [invalid parameter](#appendix-a-acceptable-values-for-parameters) | ### Applying to organizations - `apply`
Job Application
@@ -429,18 +429,18 @@ Applies to the given organization by creating a job application associated with | Command | Reason | |----------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------| -| `apply 1 --title SWE` | Apply to the **organization** at index 1, for the title of **SWE**. | -| `apply google --title Unit Tester --by 12-12-2023` | Apply to the **organization** with ID of *google** for title of **Unit Tester** by **12-12-2023**. | +| `apply 1 --title SWE`{:.language-sh} | Apply to the **organization** at index 1, for the title of **SWE**. | +| `apply google --title Unit Tester --by 12-12-2023`{:.language-sh} | Apply to the **organization** with ID of *google** for title of **Unit Tester** by **12-12-2023**. | ##### Invalid examples | Command | Reason | |---------------------------------------|-----------------------------------------------------| -| `apply 0 --title SWE` | Invalid index as index starts at 1. | -| `apply 1 --title` | Invalid as `--title` is declared but not specified. | -| `apply 1 --title SWE --by 31-31-2023` | Invalid date for deadline. | +| `apply 0 --title SWE`{:.language-sh} | Invalid index as index starts at 1. | +| `apply 1 --title`{:.language-sh} | Invalid as `--title`{:.language-sh} is declared but not specified. | +| `apply 1 --title SWE --by 31-31-2023`{:.language-sh} | Invalid date for deadline. | -### Editing job applications - `edit --application` +### Editing job applications - `edit --application`{:.language-sh}
Job Application
:trophy: Able to edit job applications associated with an organization in Jobby Intermediate
@@ -459,16 +459,16 @@ Edits the given job application according to the parameters given. | Command | Reason | |--------------------------------------------|-----------------------------------------------------------------| -| `edit --application 1 --title SRE` | Change the title of the job application at index 1 to **SRE**. | -| `edit --application 1 --status pending` | Change the status of job application at index 1 to **pending**. | +| `edit --application 1 --title SRE`{:.language-sh} | Change the title of the job application at index 1 to **SRE**. | +| `edit --application 1 --status pending`{:.language-sh} | Change the status of job application at index 1 to **pending**. | ##### Invalid examples | Command | Reason | |----------------------------------------------|---------------------------------------| -| `edit --application 0 --title SRE` | Invalid index. | -| `edit --application 1` | None of the fields to edit are given. | -| `edit --application 1 --by 31-31-2023` | The date is invalid. | +| `edit --application 0 --title SRE`{:.language-sh} | Invalid index. | +| `edit --application 1`{:.language-sh} | None of the fields to edit are given. | +| `edit --application 1 --by 31-31-2023`{:.language-sh} | The date is invalid. | ### Deleting data - `delete` @@ -487,7 +487,7 @@ The `delete` command allows you to delete contacts and job applications if they delete INDEX/ID [--recursive] ``` Deletes the contact at the given `INDEX` or `ID`. -* `--recursive` flag deletes the associated recruiter contacts and internship applications if the contact to delete is an organization. +* `--recursive`{:.language-sh} flag deletes the associated recruiter contacts and internship applications if the contact to delete is an organization. * If you wish to know more about the requirements for each parameter, check out the [given appendix](#appendix-a-acceptable-values-for-parameters). ##### Valid examples @@ -496,7 +496,7 @@ Deletes the contact at the given `INDEX` or `ID`. |------------------------|----------------------------------------------------------------------------------------| | `delete 1` | This will delete the contact at index 1. | | `delete josh` | This will delete the contact with the **ID** of **josh**. | -| `delete 1 --recursive` | This will delete a contact and all its associated recruiter contacts and applications. | +| `delete 1 --recursive`{:.language-sh} | This will delete a contact and all its associated recruiter contacts and applications. | ##### Invalid examples @@ -504,7 +504,7 @@ Deletes the contact at the given `INDEX` or `ID`. |------------------------|---------------------------------------------------------------------| | `delete 0` | Invalid index, as index starts from 1. | -#### Deleting job applications - `delete --application` +#### Deleting job applications - `delete --application`{:.language-sh} :trophy: Able to delete job applications in Jobby Intermediate @@ -519,13 +519,13 @@ Deletes the job application at the given `INDEX`. | Command | Reason | |--------------------------|---------------------------------------------------------------------| -| `delete --application 1` | This will delete the application at index 1. | +| `delete --application 1`{:.language-sh} | This will delete the application at index 1. | ##### Invalid examples | Command | Reason | |--------------------------|---------------------------------------------------| -| `delete --application 0` | Invalid index, as index starts from 1. | +| `delete --application 0`{:.language-sh} | Invalid index, as index starts from 1. | ### Listing data - `list` @@ -539,18 +539,18 @@ list [--org / --rec / --toapply] ``` Lists all contacts. If you provide a parameter, the contacts listed will be only those that fit the given parameter. -* Supplying `--org` lists only Organizations while supplying `--rec` lists only Recruiters. Specifying neither will list all contacts. +* Supplying `--org`{:.language-sh} lists only Organizations while supplying `--rec`{:.language-sh} lists only Recruiters. Specifying neither will list all contacts. -* Supplying `--toapply` lists Organizations you have not applied to. +* Supplying `--toapply`{:.language-sh} lists Organizations you have not applied to. ##### Valid examples | Command | Reason | |------------------|-------------------------------------------------------------------| | `list` | List all **contacts**. | -| `list --org` | List all **organization contacts**. | -| `list --rec` | List all **recruiter contacts**. | -| `list --toapply` | List all **organization contacts** that have not been applied to. | +| `list --org`{:.language-sh} | List all **organization contacts**. | +| `list --rec`{:.language-sh} | List all **recruiter contacts**. | +| `list --toapply`{:.language-sh} | List all **organization contacts** that have not been applied to. | ### Searching contacts - `find` @@ -596,44 +596,44 @@ sort --FLAG_TO_SORT [--ascending / --descending] ``` Sorts contacts or job applications for you by the specified flag. -`--FLAG_TO_SORT` represents a parameter of the contact or job application (i.e. `--phone` represents the phone number of a contact). +`--FLAG_TO_SORT`{:.language-sh} represents a parameter of the contact or job application (i.e. `--phone`{:.language-sh} represents the phone number of a contact). ##### Supported primary parameters (only 1 may be provided) ###### Fields for Contacts -* `--address` - Will sort alphabetically. -* `--email` - Will sort alphabetically. -* `--id` - Will sort alphabetically. -* `--name` - Will sort alphabetically. -* `--phone` - Will sort alphabetically. -* `--url` - Will sort alphabetically. +* `--address`{:.language-sh} - Will sort alphabetically. +* `--email`{:.language-sh} - Will sort alphabetically. +* `--id`{:.language-sh} - Will sort alphabetically. +* `--name`{:.language-sh} - Will sort alphabetically. +* `--phone`{:.language-sh} - Will sort alphabetically. +* `--url`{:.language-sh} - Will sort alphabetically. ###### Fields for Job Applications -* `--by` - Will sort chronologically. -* `--stage` - Will sort by stage order. -* `--stale` - Will sort chronologically. -* `--status` - Will sort by status order. -* `--title` - Will sort alphabetically. +* `--by`{:.language-sh} - Will sort chronologically. +* `--stage`{:.language-sh} - Will sort by stage order. +* `--stale`{:.language-sh} - Will sort chronologically. +* `--status`{:.language-sh} - Will sort by status order. +* `--title`{:.language-sh} - Will sort alphabetically. ###### Resetting the sort order -* `--none` - Will reset the sorting order of Contacts and Job Applications. +* `--none`{:.language-sh} - Will reset the sorting order of Contacts and Job Applications. ##### Supported secondary parameters ###### Changing the sort order -* `--ascending` - The specified flag will sort in ascending order. -* `--descending` - The specified flag will sort in descending order. +* `--ascending`{:.language-sh} - The specified flag will sort in ascending order. +* `--descending`{:.language-sh} - The specified flag will sort in descending order. -* If neither `--ascending` or `--descending` are provided, the list will be sorted in ascending order by default. +* If neither `--ascending`{:.language-sh} or `--descending`{:.language-sh} are provided, the list will be sorted in ascending order by default. -* Neither `--ascending` nor `--descending` may be specified if the flag is `--none`. +* Neither `--ascending`{:.language-sh} nor `--descending`{:.language-sh} may be specified if the flag is `--none`{:.language-sh}. * Sorting will work even if no Contacts or Job Applications exist. In that case, nothing will happen. ##### Sample demonstration -* To order your Job Applications by order of earliest deadline, you can use the command `sort --by`. +* To order your Job Applications by order of earliest deadline, you can use the command `sort --by`{:.language-sh}. * In the Application Details section of Jobby, you should see your Job Applications now ordered by most urgent deadline.
@@ -644,19 +644,19 @@ Sorts contacts or job applications for you by the specified flag. | Command | Reason | |-----------------------------|------------------------------------------------------------------------------------------------------------| -| `sort --title --ascending` | Sort **job applications** by title, in ascending alphabetical order. | -| `sort --url` | Sort **contacts** by url, in the default order - ascending alphabetical. | -| `sort --stale --descending` | Sort **job applications** by last updated time, in reverse chronological order, from most recent to least. | -| `sort --none` | Reset the sorting order of **contacts** and **job applications**. | +| `sort --title --ascending`{:.language-sh} | Sort **job applications** by title, in ascending alphabetical order. | +| `sort --url`{:.language-sh} | Sort **contacts** by url, in the default order - ascending alphabetical. | +| `sort --stale --descending`{:.language-sh} | Sort **job applications** by last updated time, in reverse chronological order, from most recent to least. | +| `sort --none`{:.language-sh} | Reset the sorting order of **contacts** and **job applications**. | ##### Invalid examples | Command | Reason | |----------------------------|---------------------------------------------| | `sort` | No field provided. | -| `sort --org` | Invalid field. | -| `sort --none --descending` | `--none` and `--descending` both specified. | -| `sort --title --name` | More than 1 field specified. | +| `sort --org`{:.language-sh} | Invalid field. | +| `sort --none --descending`{:.language-sh} | `--none`{:.language-sh} and `--descending`{:.language-sh} both specified. | +| `sort --title --name`{:.language-sh} | More than 1 field specified. | ### Reminding about deadlines - `remind` @@ -672,7 +672,7 @@ remind --earliest / --latest Reminds you of upcoming deadlines for job applications. ##### Sample demonstration -* To see your application deadlines from the earliest to latest, use the command `remind --earliest`. +* To see your application deadlines from the earliest to latest, use the command `remind --earliest`{:.language-sh}. ![Remind Earliest](images/starter-guide/remind-earliest.jpg) @@ -680,8 +680,8 @@ Reminds you of upcoming deadlines for job applications. | Command | Reason | |---------------------|--------------------------------------------------------------------------------------| -| `remind --earliest` | List the application deadlines in order of urgency, from earliest to latest. | -| `remind --latest` | List the application deadlines in order of reverse urgency, from latest to earliest. | +| `remind --earliest`{:.language-sh} | List the application deadlines in order of urgency, from earliest to latest. | +| `remind --latest`{:.language-sh} | List the application deadlines in order of reverse urgency, from latest to earliest. | ##### Invalid examples @@ -739,13 +739,13 @@ Exits the program. | Action | Format, Examples | |----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **Add Organization** | `add --org --name NAME [--id ID] [--phone NUMBER] [--email EMAIL] [--url URL] [--address ADDRESS] [--tag TAG]...`
e.g., `add --org --name NUS --phone 0123456789 --email example@nus.edu.sg --url https://www.nus.edu.sg/` | -| **Add Recruiter** | `add --rec --name NAME [--id ID] [--oid ORG_ID] [--phone NUMBER] [--email EMAIL] [--url URL] [--address ADDRESS] [--tag TAG]...`
e.g., `add --rec --name John Doe --oid paypal-sg` | -| **Delete Contact** | `delete INDEX/ID [--recursive]`
e.g., `delete 3`, `delete id-55tg` | -| **Edit Contact** | `edit INDEX/ID [--name NAME] [--id ID] [--phone PHONE] [--email EMAIL] [--url URL] [--address ADDRESS] [--tag TAG]...` | +| **Add Organization** | `add --org --name NAME [--id ID] [--phone NUMBER] [--email EMAIL] [--url URL] [--address ADDRESS] [--tag TAG]...`{:.language-sh}
e.g., `add --org --name NUS --phone 0123456789 --email example@nus.edu.sg --url https://www.nus.edu.sg/`{:.language-sh} | +| **Add Recruiter** | `add --rec --name NAME [--id ID] [--oid ORG_ID] [--phone NUMBER] [--email EMAIL] [--url URL] [--address ADDRESS] [--tag TAG]...`{:.language-sh}
e.g., `add --rec --name John Doe --oid paypal-sg`{:.language-sh} | +| **Delete Contact** | `delete INDEX/ID [--recursive]`{:.language-sh}
e.g., `delete 3`, `delete id-55tg` | +| **Edit Contact** | `edit INDEX/ID [--name NAME] [--id ID] [--phone PHONE] [--email EMAIL] [--url URL] [--address ADDRESS] [--tag TAG]...`{:.language-sh} | | **Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` | -| **List** | `list [--org/--rec/--toapply]` | -| **Sort Contacts** | `sort --address/--email/--id/--name/--phone/--url [--ascending/--descending]` | +| **List** | `list [--org/--rec/--toapply]`{:.language-sh} | +| **Sort Contacts** | `sort --address/--email/--id/--name/--phone/--url [--ascending/--descending]`{:.language-sh} | ### Commands for Handling Job Applications @@ -754,10 +754,10 @@ Exits the program. | Action | Format, Examples | |------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **Delete Application** | `delete --application INDEX`
e.g., `delete --application 2` | -| **Edit Application** | `edit --application INDEX [--title TITLE] [--description DESCRIPTION] [--by DEADLINE] [--status STATUS] [--stage STAGE]`
e.g., `edit --application 2 --title Analyst` | -| **Apply** | `apply INDEX/ID --title TITLE [--description DESCRIPTION] [--by DEADLINE] [--stage STAGE] [--status STATUS]` | -| **Sort Applications** | `sort --by/--stage/--stale/--status/--title [--ascending/--descending]` | +| **Delete Application** | `delete --application INDEX`{:.language-sh}
e.g., `delete --application 2`{:.language-sh} | +| **Edit Application** | `edit --application INDEX [--title TITLE] [--description DESCRIPTION] [--by DEADLINE] [--status STATUS] [--stage STAGE]`{:.language-sh}
e.g., `edit --application 2 --title Analyst`{:.language-sh} | +| **Apply** | `apply INDEX/ID --title TITLE [--description DESCRIPTION] [--by DEADLINE] [--stage STAGE] [--status STATUS]`{:.language-sh} | +| **Sort Applications** | `sort --by/--stage/--stale/--status/--title [--ascending/--descending]`{:.language-sh} | ### Other Commands @@ -786,20 +786,20 @@ Exits the program. | Parameter | Used by | Requirements | Examples | |-----------|---------|--------------|----------| -| `INDEX` | [`edit`](#editing-contacts---edit)
[`apply`](#applying-to-organizations---apply)
[`edit --application`](#editing-job-applications---edit---application)
[`delete`](#deleting-contacts---delete)
[`delete --application`](#deleting-job-applications---delete---application) | A valid index can accept any positive integer up to the number of items displayed in the contact or job application list where applicable. | `1`
`10` | -| `NAME` | [`add --org`](#adding-organizations---add---org)
[`add --rec`](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid name can accept any non-empty value. | `Ryan Koh`
`小明` | -| `ID` | [`add --org`](#adding-organizations---add---org)
[`add --rec`](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit)
[`apply`](#applying-to-organizations---apply)
[`delete`](#deleting-contacts---delete) | A valid ID has to start with a letter.

It can consist of alphanumeric and basic symbols (i.e. `a-z`, `A-Z`, `0-9`, `-`, `_`) | `woogle123`
`ryan_soc-rec` | -| `NUMBER` | [`add --org`](#adding-organizations---add---org)
[`add --rec`](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid phone number can consist of only numbers with no whitespace.

It must be at least 3 digits. | `999`
`91824137` | -| `EMAIL` | [`add --org`](#adding-organizations---add---org)
[`add --rec`](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid email should be in the form of `local-part@domain` where the `local-part` and `domain` must be separated by a single **@**.

The `local-part` can consist of any character except whitespace.

The `domain` name can comprise of one or more labels separated by periods, and each label can include any character except whitespace. The last `domain` label must be a minimum of two characters long. | `ryankoh@nus`
`ryan-koh@nus.edu.sg` | -| `URL` | [`add --org`](#adding-organizations---add---org)
[`add --rec`](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid url should include a part in the form of `domain.tld` where the `domain` and the `tld` (top level domain) must be separated by a period. | `example.com`
`example.more.com`
`https://example.com`
`example.com/more` | -| `ADDRESS`| [`add --org`](#adding-organizations---add---org)
[`add --rec`](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid address can accept any non-empty value.

For a contact, it designates its physical address. | `21 Lower Kent Ridge Rd` | -| `TAG` | [`add --org`](#adding-organizations---add---org)
[`add --rec`](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid tag can consist of only alphanumeric characters. | `internship`
`network`
`parttime`
`jobPortal` | -| `ORG_ID` | [`add --rec`](#adding-recruiters---add---rec) | A valid organization ID is subject to the same requirements as the ID parameter.

It must belong to an Organization contact in the address book. | `woogle123`
`meta_sg-1` | -| `TITLE` | [`apply`](#applying-to-organizations---apply)
[`edit --application`](#editing-job-applications---edit---application) | A valid title can accept multiple words separated with spaces, as long as the characters are alphanumeric. | `Software Engineer`
`Level 3 Engineer` | -| `DESCRIPTION` | [`apply`](#applying-to-organizations---apply)
[`edit --application`](#editing-job-applications---edit---application) | A valid description can accept any non-empty value. | `Senior Role`
`Hourly rate: $25` | -| `DEADLINE` | [`apply`](#applying-to-organizations---apply)
[`edit --application`](#editing-job-applications---edit---application) | A valid deadline should be a date in the form of `DD-MM-YYYY`.

The day (`DD`) and month (`MM`) can be either single or double digits. | `09-02-2022`
`9-2-2022`
`19-11-2022` | -| `STAGE` | [`apply`](#applying-to-organizations---apply)
[`edit --application`](#editing-job-applications---edit---application) | A valid job application stage can accept only one of the three values: `resume`, `online assessment`, `interview`.

The values are ranked in the order shown. | `resume`
`online assessment`
`interview` | -| `STATUS` | [`apply`](#applying-to-organizations---apply)
[`edit --application`](#editing-job-applications---edit---application) | A valid job application status can accept only one of the four values: `pending`, `offered`, `accepted`, `turned down`.

The values are ranked in the order shown. | `pending`
`offered`
`accepted`
`turned down` | +| `INDEX` | [`edit`](#editing-contacts---edit)
[`apply`](#applying-to-organizations---apply)
[`edit --application`{:.language-sh}](#editing-job-applications---edit---application)
[`delete`](#deleting-contacts---delete)
[`delete --application`{:.language-sh}](#deleting-job-applications---delete---application) | A valid index can accept any positive integer up to the number of items displayed in the contact or job application list where applicable. | `1`
`10` | +| `NAME` | [`add --org`{:.language-sh}](#adding-organizations---add---org)
[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid name can accept any non-empty value. | `Ryan Koh`
`小明` | +| `ID` | [`add --org`{:.language-sh}](#adding-organizations---add---org)
[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit)
[`apply`](#applying-to-organizations---apply)
[`delete`](#deleting-contacts---delete) | A valid ID has to start with a letter.

It can consist of alphanumeric and basic symbols (i.e. `a-z`, `A-Z`, `0-9`, `-`, `_`) | `woogle123`
`ryan_soc-rec` | +| `NUMBER` | [`add --org`{:.language-sh}](#adding-organizations---add---org)
[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid phone number can consist of only numbers with no whitespace.

It must be at least 3 digits. | `999`
`91824137` | +| `EMAIL` | [`add --org`{:.language-sh}](#adding-organizations---add---org)
[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid email should be in the form of `local-part@domain` where the `local-part` and `domain` must be separated by a single **@**.

The `local-part` can consist of any character except whitespace.

The `domain` name can comprise of one or more labels separated by periods, and each label can include any character except whitespace. The last `domain` label must be a minimum of two characters long. | `ryankoh@nus`
`ryan-koh@nus.edu.sg` | +| `URL` | [`add --org`{:.language-sh}](#adding-organizations---add---org)
[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid url should include a part in the form of `domain.tld` where the `domain` and the `tld` (top level domain) must be separated by a period. | `example.com`
`example.more.com`
`https://example.com`
`example.com/more` | +| `ADDRESS`| [`add --org`{:.language-sh}](#adding-organizations---add---org)
[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid address can accept any non-empty value.

For a contact, it designates its physical address. | `21 Lower Kent Ridge Rd` | +| `TAG` | [`add --org`{:.language-sh}](#adding-organizations---add---org)
[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid tag can consist of only alphanumeric characters. | `internship`
`network`
`parttime`
`jobPortal` | +| `ORG_ID` | [`add --rec`{:.language-sh}](#adding-recruiters---add---rec) | A valid organization ID is subject to the same requirements as the ID parameter.

It must belong to an Organization contact in the address book. | `woogle123`
`meta_sg-1` | +| `TITLE` | [`apply`](#applying-to-organizations---apply)
[`edit --application`{:.language-sh}](#editing-job-applications---edit---application) | A valid title can accept multiple words separated with spaces, as long as the characters are alphanumeric. | `Software Engineer`
`Level 3 Engineer` | +| `DESCRIPTION` | [`apply`](#applying-to-organizations---apply)
[`edit --application`{:.language-sh}](#editing-job-applications---edit---application) | A valid description can accept any non-empty value. | `Senior Role`
`Hourly rate: $25` | +| `DEADLINE` | [`apply`](#applying-to-organizations---apply)
[`edit --application`{:.language-sh}](#editing-job-applications---edit---application) | A valid deadline should be a date in the form of `DD-MM-YYYY`.

The day (`DD`) and month (`MM`) can be either single or double digits. | `09-02-2022`
`9-2-2022`
`19-11-2022` | +| `STAGE` | [`apply`](#applying-to-organizations---apply)
[`edit --application`{:.language-sh}](#editing-job-applications---edit---application) | A valid job application stage can accept only one of the three values: `resume`, `online assessment`, `interview`.

The values are ranked in the order shown. | `resume`
`online assessment`
`interview` | +| `STATUS` | [`apply`](#applying-to-organizations---apply)
[`edit --application`{:.language-sh}](#editing-job-applications---edit---application) | A valid job application status can accept only one of the four values: `pending`, `offered`, `accepted`, `turned down`.

The values are ranked in the order shown. | `pending`
`offered`
`accepted`
`turned down` | | `KEYWORD` | [`find`](#searching-contacts---find) | A valid keyword is a single word that can accept any non-empty value. | `software`
`Ryan` | @@ -829,6 +829,6 @@ Exits the program. 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. **When requesting to sort applications after a call to `list --rec`**, the command will succeed but display nothing, since no organizations are currently listed, and so no linked applications will display. The remedy is to call `list` before sorting applications and calling the sort command once more. +2. **When requesting to sort applications after a call to `list --rec`{:.language-sh}**, the command will succeed but display nothing, since no organizations are currently listed, and so no linked applications will display. The remedy is to call `list` before sorting applications and calling the sort command once more. 3. Parameter names use either the `-` or `--` prefix, but **all commands as of the current version only use the `--` prefix.** The `-` prefix is currently unused, but in future updates it may become relevant. From 1d2b4dd9351025b5a783022112dd88025a9c71ec Mon Sep 17 00:00:00 2001 From: Wern Date: Mon, 13 Nov 2023 23:54:43 +0800 Subject: [PATCH 15/23] Fix styles --- docs/_sass/minima/custom-styles.scss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/_sass/minima/custom-styles.scss b/docs/_sass/minima/custom-styles.scss index 79687640222..1da9573ec20 100644 --- a/docs/_sass/minima/custom-styles.scss +++ b/docs/_sass/minima/custom-styles.scss @@ -56,6 +56,7 @@ img.emoji { } // Syntax highlighting term overrides +// - Multiline .language-sh, .language-bash, .language-shell { .highlight code { .nb { @@ -68,6 +69,13 @@ img.emoji { } } } +// - Inline +code.language-plaintext, code.language-sh, code.language-bash, code.language-shell { + .nt { + /* do not wrap --flags */ + white-space: nowrap; + } +} // Pill styles .pill { From fee09648230d424d092437fbaa54e572831b4ae2 Mon Sep 17 00:00:00 2001 From: Wern Date: Tue, 14 Nov 2023 00:00:27 +0800 Subject: [PATCH 16/23] Add spacing for syntax highlighting to detect flags --- docs/UserGuide.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index aaefb8e2e33..a4dcae4151f 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -245,7 +245,7 @@ Throughout this guide, you will find symbols and placeholders used to describe a * These may be included in the parameter names or value description. - * e.g., `--a/--b`{:.language-sh} means either `--a`{:.language-sh} or `--b`{:.language-sh} but not `--a --b`{:.language-sh}. + * e.g., `--a / --b`{:.language-sh} means either `--a`{:.language-sh} or `--b`{:.language-sh} but not `--a --b`{:.language-sh}. * **Terms surrounded by `[` and `]`** @@ -744,8 +744,8 @@ Exits the program. | **Delete Contact** | `delete INDEX/ID [--recursive]`{:.language-sh}
e.g., `delete 3`, `delete id-55tg` | | **Edit Contact** | `edit INDEX/ID [--name NAME] [--id ID] [--phone PHONE] [--email EMAIL] [--url URL] [--address ADDRESS] [--tag TAG]...`{:.language-sh} | | **Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` | -| **List** | `list [--org/--rec/--toapply]`{:.language-sh} | -| **Sort Contacts** | `sort --address/--email/--id/--name/--phone/--url [--ascending/--descending]`{:.language-sh} | +| **List** | `list [--org / --rec / --toapply]`{:.language-sh} | +| **Sort Contacts** | `sort --address / --email / --id / --name / --phone / --url [--ascending / --descending]`{:.language-sh} | ### Commands for Handling Job Applications @@ -757,7 +757,7 @@ Exits the program. | **Delete Application** | `delete --application INDEX`{:.language-sh}
e.g., `delete --application 2`{:.language-sh} | | **Edit Application** | `edit --application INDEX [--title TITLE] [--description DESCRIPTION] [--by DEADLINE] [--status STATUS] [--stage STAGE]`{:.language-sh}
e.g., `edit --application 2 --title Analyst`{:.language-sh} | | **Apply** | `apply INDEX/ID --title TITLE [--description DESCRIPTION] [--by DEADLINE] [--stage STAGE] [--status STATUS]`{:.language-sh} | -| **Sort Applications** | `sort --by/--stage/--stale/--status/--title [--ascending/--descending]`{:.language-sh} | +| **Sort Applications** | `sort --by / --stage / --stale / --status / --title [--ascending / --descending]`{:.language-sh} | ### Other Commands From 057aae281f525c828e84cc6f961e9df1b3d8f32a Mon Sep 17 00:00:00 2001 From: Wern Date: Tue, 14 Nov 2023 00:26:49 +0800 Subject: [PATCH 17/23] Update UG --- docs/UserGuide.md | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index a4dcae4151f..666e229a97d 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -184,18 +184,18 @@ Here is how you can use [`apply`](#applying-to-organizations---apply) command to You have successfully added your job application to **Google**! **Congratulations!** You have run through the basics of Jobby. We hope that this tutorial has given you an understanding of the -basic workflow of Jobby. However, there are still many features that we have yet introduced. Please refer to the [Using Jobby Section](#using-jobby) to understand -the command structure of Jobby, or visit [Features Section](#features) to see the full capabilities of Jobby! +basic workflow of Jobby. However, there are still many features that we have yet introduced. Please refer to the [Using Jobby](#using-jobby) section to understand +Jobby's command structures, or visit the [Features](#features) section to see the full capabilities of Jobby! -------------------------------------------------------------------------------------------------------------------- ## Using Jobby -This section explains the details of how we can interact with Jobby. +This section explains how we can understand and interact with Jobby via commands. If you're looking for the list of available commands, check out the [Features](#features) section instead. -### Command Structure +### Understanding the Command Structure :trophy: How to understand and write Jobby commands Beginner @@ -227,11 +227,11 @@ Any extra parameters and values to commands that don't accept them will either b
-### Command Explanations +### Reading Command Formats -:trophy: How to interpret this guide's command explanations Beginner +:trophy: How to interpret Jobby-formatted command explanations Beginner -Throughout this guide, you will find symbols and placeholders used to describe a command format. They are: +Throughout this guide and within Jobby itself, you will find symbols and placeholders used to describe a command format. They are: * **Words in `UPPER_CASE`** @@ -259,9 +259,16 @@ Throughout this guide, you will find symbols and placeholders used to describe a * e.g., `[--tag TAG]...`{:.language-sh} means `--tag`{:.language-sh} and its value can be repeated from 0 to any number of times. +* **Terms surrounded by `<` and `>`** + * A high level description of the parameter or option. -### Command Autocomplete + * e.g., if will see something like `< add some text here >`, which means you should replace it with your own text. + +Parameters may have certain value format restrictions - Jobby will let you know if you do not meet a requirement when you input your command. Optionally, you may also refer to their details in [Appendix A](#appendix-a--acceptable-values-for-parameters) later. + + +### Autocompleting Commands :trophy: How to use Jobby's command autocompletion Beginner From ed830b365bb61496e93c7883ca8e760c39a1345e Mon Sep 17 00:00:00 2001 From: Wern Date: Tue, 14 Nov 2023 00:33:26 +0800 Subject: [PATCH 18/23] Update UG Glossary --- docs/UserGuide.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 666e229a97d..d2ce2aa6ef5 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -779,13 +779,14 @@ Exits the program. ## Glossary -| Term | Definition | -|------|------------| -| **Top Level Domain** | A Top Level Domain (TLD) is the part of the website address where it comes after the last dot (i.e. ".com", ".org", ".net") and before the first slash. (E.g. www.example.**com**/path). | -| **Whitespace** | In the context of this application, a whitespace is any number of spaces or tabs that is in the input. | -| **Contact** | A contact in Jobby is can be an **organization** or a **recruiter**. | -| **Substring** | A substring is a contiguous sequence of characters within a string.
e.g. "pp" is a substring of "apple", "mac" is a substring of "macDonald" and "intimacy" | - +| Term | Definition | +|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Index** | An index is a number that is used to identify a contact or job application in a list.

(e.g. `2` would be the index of the contact labelled **2.** in the contacts list). | +| **Whitespace** | In the context of this application, a whitespace is any number of spaces that is in the input. | +| **Contact** | A contact in Jobby is can be an Organization or a Recruiter. | +| **Substring** | A substring is a contiguous sequence of characters within a string

(e.g. "pp" is a substring of "apple", "mac" is a substring of "macDonald" and "intimacy"). | +| **Subsequence** | A subsequence is a sequence obtainable from another sequence by deleting some or no elements without changing the order of the remaining elements

(e.g. "abc", "1b2", "123" are all subsequences of "a1b2c3"). | +| **Top Level Domain** | A Top Level Domain (TLD) is the part of the website address where it comes after the last dot (i.e. ".com", ".org", ".net") and before the first slash

(e.g. www.example.**com**/path). | ## Appendices From 8e4aa00839d9e1c45e6e93d735dc9124ec20448a Mon Sep 17 00:00:00 2001 From: Wern Date: Tue, 14 Nov 2023 00:39:08 +0800 Subject: [PATCH 19/23] Update UG parameter separation --- docs/UserGuide.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index d2ce2aa6ef5..f3d50486459 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -794,20 +794,20 @@ Exits the program. | Parameter | Used by | Requirements | Examples | |-----------|---------|--------------|----------| -| `INDEX` | [`edit`](#editing-contacts---edit)
[`apply`](#applying-to-organizations---apply)
[`edit --application`{:.language-sh}](#editing-job-applications---edit---application)
[`delete`](#deleting-contacts---delete)
[`delete --application`{:.language-sh}](#deleting-job-applications---delete---application) | A valid index can accept any positive integer up to the number of items displayed in the contact or job application list where applicable. | `1`
`10` | -| `NAME` | [`add --org`{:.language-sh}](#adding-organizations---add---org)
[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid name can accept any non-empty value. | `Ryan Koh`
`小明` | -| `ID` | [`add --org`{:.language-sh}](#adding-organizations---add---org)
[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit)
[`apply`](#applying-to-organizations---apply)
[`delete`](#deleting-contacts---delete) | A valid ID has to start with a letter.

It can consist of alphanumeric and basic symbols (i.e. `a-z`, `A-Z`, `0-9`, `-`, `_`) | `woogle123`
`ryan_soc-rec` | -| `NUMBER` | [`add --org`{:.language-sh}](#adding-organizations---add---org)
[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid phone number can consist of only numbers with no whitespace.

It must be at least 3 digits. | `999`
`91824137` | -| `EMAIL` | [`add --org`{:.language-sh}](#adding-organizations---add---org)
[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid email should be in the form of `local-part@domain` where the `local-part` and `domain` must be separated by a single **@**.

The `local-part` can consist of any character except whitespace.

The `domain` name can comprise of one or more labels separated by periods, and each label can include any character except whitespace. The last `domain` label must be a minimum of two characters long. | `ryankoh@nus`
`ryan-koh@nus.edu.sg` | -| `URL` | [`add --org`{:.language-sh}](#adding-organizations---add---org)
[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid url should include a part in the form of `domain.tld` where the `domain` and the `tld` (top level domain) must be separated by a period. | `example.com`
`example.more.com`
`https://example.com`
`example.com/more` | -| `ADDRESS`| [`add --org`{:.language-sh}](#adding-organizations---add---org)
[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid address can accept any non-empty value.

For a contact, it designates its physical address. | `21 Lower Kent Ridge Rd` | -| `TAG` | [`add --org`{:.language-sh}](#adding-organizations---add---org)
[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)
[`edit`](#editing-contacts---edit) | A valid tag can consist of only alphanumeric characters. | `internship`
`network`
`parttime`
`jobPortal` | +| `INDEX` | [`edit`](#editing-contacts---edit)

[`apply`](#applying-to-organizations---apply)

[`edit --application`{:.language-sh}](#editing-job-applications---edit---application)

[`delete`](#deleting-contacts---delete)

[`delete --application`{:.language-sh}](#deleting-job-applications---delete---application) | A valid index can accept any positive integer up to the number of items displayed in the contact or job application list where applicable. | `1`
`10` | +| `NAME` | [`add --org`{:.language-sh}](#adding-organizations---add---org)

[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)

[`edit`](#editing-contacts---edit) | A valid name can accept any non-empty value. | `Ryan Koh`
`小明` | +| `ID` | [`add --org`{:.language-sh}](#adding-organizations---add---org)

[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)

[`edit`](#editing-contacts---edit)

[`apply`](#applying-to-organizations---apply)

[`delete`](#deleting-contacts---delete) | A valid ID has to start with a letter.

It can consist of alphanumeric and basic symbols (i.e. `a-z`, `A-Z`, `0-9`, `-`, `_`) | `woogle123`
`ryan_soc-rec` | +| `NUMBER` | [`add --org`{:.language-sh}](#adding-organizations---add---org)

[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)

[`edit`](#editing-contacts---edit) | A valid phone number can consist of only numbers with no whitespace.

It must be at least 3 digits. | `999`
`91824137` | +| `EMAIL` | [`add --org`{:.language-sh}](#adding-organizations---add---org)

[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)

[`edit`](#editing-contacts---edit) | A valid email should be in the form of `local-part@domain` where the `local-part` and `domain` must be separated by a single **@**.

The `local-part` can consist of any character except whitespace.

The `domain` name can comprise of one or more labels separated by periods, and each label can include any character except whitespace. The last `domain` label must be a minimum of two characters long. | `ryankoh@nus`
`ryan-koh@nus.edu.sg` | +| `URL` | [`add --org`{:.language-sh}](#adding-organizations---add---org)

[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)

[`edit`](#editing-contacts---edit) | A valid url should include a part in the form of `domain.tld` where the `domain` and the `tld` (top level domain) must be separated by a period. | `example.com`
`example.more.com`
`https://example.com`
`example.com/more` | +| `ADDRESS`| [`add --org`{:.language-sh}](#adding-organizations---add---org)

[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)

[`edit`](#editing-contacts---edit) | A valid address can accept any non-empty value.

For a contact, it designates its physical address. | `21 Lower Kent Ridge Rd` | +| `TAG` | [`add --org`{:.language-sh}](#adding-organizations---add---org)

[`add --rec`{:.language-sh}](#adding-recruiters---add---rec)

[`edit`](#editing-contacts---edit) | A valid tag can consist of only alphanumeric characters. | `internship`
`network`
`parttime`
`jobPortal` | | `ORG_ID` | [`add --rec`{:.language-sh}](#adding-recruiters---add---rec) | A valid organization ID is subject to the same requirements as the ID parameter.

It must belong to an Organization contact in the address book. | `woogle123`
`meta_sg-1` | -| `TITLE` | [`apply`](#applying-to-organizations---apply)
[`edit --application`{:.language-sh}](#editing-job-applications---edit---application) | A valid title can accept multiple words separated with spaces, as long as the characters are alphanumeric. | `Software Engineer`
`Level 3 Engineer` | -| `DESCRIPTION` | [`apply`](#applying-to-organizations---apply)
[`edit --application`{:.language-sh}](#editing-job-applications---edit---application) | A valid description can accept any non-empty value. | `Senior Role`
`Hourly rate: $25` | -| `DEADLINE` | [`apply`](#applying-to-organizations---apply)
[`edit --application`{:.language-sh}](#editing-job-applications---edit---application) | A valid deadline should be a date in the form of `DD-MM-YYYY`.

The day (`DD`) and month (`MM`) can be either single or double digits. | `09-02-2022`
`9-2-2022`
`19-11-2022` | -| `STAGE` | [`apply`](#applying-to-organizations---apply)
[`edit --application`{:.language-sh}](#editing-job-applications---edit---application) | A valid job application stage can accept only one of the three values: `resume`, `online assessment`, `interview`.

The values are ranked in the order shown. | `resume`
`online assessment`
`interview` | -| `STATUS` | [`apply`](#applying-to-organizations---apply)
[`edit --application`{:.language-sh}](#editing-job-applications---edit---application) | A valid job application status can accept only one of the four values: `pending`, `offered`, `accepted`, `turned down`.

The values are ranked in the order shown. | `pending`
`offered`
`accepted`
`turned down` | +| `TITLE` | [`apply`](#applying-to-organizations---apply)

[`edit --application`{:.language-sh}](#editing-job-applications---edit---application) | A valid title can accept multiple words separated with spaces, as long as the characters are alphanumeric. | `Software Engineer`
`Level 3 Engineer` | +| `DESCRIPTION` | [`apply`](#applying-to-organizations---apply)

[`edit --application`{:.language-sh}](#editing-job-applications---edit---application) | A valid description can accept any non-empty value. | `Senior Role`
`Hourly rate: $25` | +| `DEADLINE` | [`apply`](#applying-to-organizations---apply)

[`edit --application`{:.language-sh}](#editing-job-applications---edit---application) | A valid deadline should be a date in the form of `DD-MM-YYYY`.

The day (`DD`) and month (`MM`) can be either single or double digits. | `09-02-2022`
`9-2-2022`
`19-11-2022` | +| `STAGE` | [`apply`](#applying-to-organizations---apply)

[`edit --application`{:.language-sh}](#editing-job-applications---edit---application) | A valid job application stage can accept only one of the three values: `resume`, `online assessment`, `interview`.

The values are ranked in the order shown. | `resume`
`online assessment`
`interview` | +| `STATUS` | [`apply`](#applying-to-organizations---apply)

[`edit --application`{:.language-sh}](#editing-job-applications---edit---application) | A valid job application status can accept only one of the four values: `pending`, `offered`, `accepted`, `turned down`.

The values are ranked in the order shown. | `pending`
`offered`
`accepted`
`turned down` | | `KEYWORD` | [`find`](#searching-contacts---find) | A valid keyword is a single word that can accept any non-empty value. | `software`
`Ryan` | From b33fa0c9e13ed032a4c226a2aef034e78820d5e2 Mon Sep 17 00:00:00 2001 From: Wern Date: Tue, 14 Nov 2023 00:44:25 +0800 Subject: [PATCH 20/23] Fix typo --- docs/UserGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index f3d50486459..94ca5236c83 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -263,7 +263,7 @@ Throughout this guide and within Jobby itself, you will find symbols and placeho * A high level description of the parameter or option. - * e.g., if will see something like `< add some text here >`, which means you should replace it with your own text. + * e.g., if you see something like `< add some text here >`, it means you should replace it with your own text. Parameters may have certain value format restrictions - Jobby will let you know if you do not meet a requirement when you input your command. Optionally, you may also refer to their details in [Appendix A](#appendix-a--acceptable-values-for-parameters) later. From f40a0dbc157f1ca3a88b13fc36b2f770b36a4360 Mon Sep 17 00:00:00 2001 From: Wern Date: Tue, 14 Nov 2023 00:47:52 +0800 Subject: [PATCH 21/23] Fix pills --- docs/UserGuide.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 94ca5236c83..7742a7b89b3 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -393,7 +393,8 @@ Adds a recruiter contact with the details given to the command. ### Editing contacts - `edit` -
Organization Recruiter

+
Organization Recruiter
+ :trophy: How to edit organization or recruiter info in Jobby Intermediate
:information_source: Assumes that you have read the `add` command documentation for contacts.
@@ -714,10 +715,9 @@ Shows a message explaining how to access the help page. ### Clearing all data - `clear`
Organization Recruiter Job Application
+:trophy: How to clear all contacts and job applications in Jobby Intermediate
:warning: The deletion of all data is permanent and there is no way to undo it. -:trophy: How to clear all contacts and job applications in Jobby Intermediate - ##### Format ```sh clear From a460c1b17aaad5b8b90137646750bee8928d189c Mon Sep 17 00:00:00 2001 From: Wern Date: Tue, 14 Nov 2023 00:53:08 +0800 Subject: [PATCH 22/23] Update style to not page break within a table row --- docs/_sass/minima/_layout.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/_sass/minima/_layout.scss b/docs/_sass/minima/_layout.scss index 0052216d00f..7e3ffb77e6c 100644 --- a/docs/_sass/minima/_layout.scss +++ b/docs/_sass/minima/_layout.scss @@ -370,6 +370,8 @@ page-break-before: always !important; } - + tr { + page-break-inside: avoid; + } } From f252a14090f9411889e7842933387a24ac92af0d Mon Sep 17 00:00:00 2001 From: Wern Date: Tue, 14 Nov 2023 00:56:58 +0800 Subject: [PATCH 23/23] Fix phrasing --- docs/UserGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 7742a7b89b3..62c08774912 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -839,4 +839,4 @@ Exits the program. 2. **When requesting to sort applications after a call to `list --rec`{:.language-sh}**, the command will succeed but display nothing, since no organizations are currently listed, and so no linked applications will display. The remedy is to call `list` before sorting applications and calling the sort command once more. -3. Parameter names use either the `-` or `--` prefix, but **all commands as of the current version only use the `--` prefix.** The `-` prefix is currently unused, but in future updates it may become relevant. +3. Parameter names use either the `-` or `--` prefix, but **all commands as of the current version only use the `--` prefix.** While the `-` prefix is currently unused, it is reserved (so user input cannot take that format), and it will be relevant in future updates.