:bulb: **Tip:**
-A person can have any number of tags (including 0)
+##### Valid examples
+
+| Command | Reason |
+|------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------|
+| `add --org --name J&J`{:.language-sh} | Adds an organization **J&J**. |
+| `add --org --name Google --id g-sg --phone 98765432 `{:.language-sh} | Adds 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} | Adds an organization **Examination NUS** with other flags. |
+
+
+##### Invalid examples
+
+| Command | Reason |
+|-------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|
+| `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`{:.language-sh}
+
+
+
+##### Format
+```sh
+add --rec --name NAME [-id ID] [--oid ORG_ID] [--phone NUMBER] [--email EMAIL] [--url URL] [--address ADDRESS] [--tag TAG]...
+```
+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
in the contacts list, make sure you include `--oid`{:.language-sh} and pass in the `ID` of the
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`{:.language-sh}, you should see a new
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
-Examples:
-* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01`
-* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal`
+##### Valid examples
-### Listing all persons : `list`
+| Command | Reason |
+|-------|----------|
+| `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. |
-Shows a list of all persons in the address book.
+##### Invalid examples
-Format: `list`
+| Command | Reason |
+|---------|--------|
+| `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 a person : `edit`
-Edits an existing person in the address book.
+### Editing contacts - `edit`
+
-* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …
-* At least one of the optional fields must be provided.
-* Existing values will be updated to the input values.
-* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative.
-* You can remove all the person’s tags by typing `t/` without
- specifying any tags after it.
+##### Format
+```sh
+edit INDEX/ID [--name NAME] [--id ID] [--phone PHONE] [--email EMAIL] [--url URL] [--address ADDRESS] [--tag TAG]...
+```
+Edits the given contact according to the parameters given.
+* You can supply more than one parameter to change multiple details of a contact in one command.
+* If you wish to know more about the requirements for each parameter, check out the [given appendix](#appendix-a-acceptable-values-for-parameters).
-Examples:
-* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively.
-* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags.
+##### Valid examples
-### Locating persons by name: `find`
+| Command | Reason |
+|-----------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|
+| `edit google --phone 91292951`{:.language-sh} | Changes phone number of organization with **ID** google to **91292951**. |
+| `edit 1 --name Jane Street`{:.language-sh} | Changes 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. |
-Finds persons whose names contain any of the given keywords.
+##### Invalid examples
-Format: `find KEYWORD [MORE_KEYWORDS]`
+| Command | Reason |
+|-----------------------------------------|-------------------------------------------------------------------------------------|
+| `edit google --phone 8124!@#$`{:.language-sh} | `--phone`{:.language-sh} has an [invalid parameter](#appendix-a-acceptable-values-for-parameters) |
-* The search is case-insensitive. e.g `hans` will match `Hans`
-* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`
-* Only the name is searched.
-* Only full words will be matched e.g. `Han` will not match `Hans`
-* Persons matching at least one keyword will be returned (i.e. `OR` search).
- e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`
+### Deleting contacts - `delete`
+
-Examples:
-* `find John` returns `john` and `John Doe`
-* `find alex david` returns `Alex Yeoh`, `David Li`
-Deletes the specified person from the address book.
+##### Format
+```sh
+delete INDEX/ID [--recursive]
+```
+Deletes the contact at the given `INDEX` or `ID`.
+* `--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).
-Format: `delete INDEX`
+##### Valid examples
-* Deletes the person at the specified `INDEX`.
-* The index refers to the index number shown in the displayed person list.
-* The index **must be a positive integer** 1, 2, 3, …
+| Command | Reason |
+|------------------------|----------------------------------------------------------------------------------------|
+| `delete 1` | Deletes the contact at index 1. |
+| `delete josh` | Deletes the contact with the **ID** of **josh**. |
+| `delete 1 --recursive`{:.language-sh} | Deletes a contact and all its associated recruiter contacts and applications. |
-Examples:
-* `list` followed by `delete 2` deletes the 2nd person in the address book.
-* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command.
+##### Invalid examples
-### Clearing all entries : `clear`
+| Command | Reason |
+|------------------------|---------------------------------------------------------------------|
+| `delete 0` | Invalid index, as index starts from 1. |
-Clears all entries from the address book.
+### Applying to organizations - `apply`
+
-### Exiting the program : `exit`
+##### Format
+```sh
+apply INDEX/ID --title TITLE [--description DESCRIPTION] [--by DEADLINE: DD-MM-YYYY] [--stage APPLICATION STAGE: resume | online assessment | interview] [--status STATUS: pending | offered | accepted | turned down]
+```
+Applies to the given organization by creating a job application associated with it.
+* If you wish to know more about the requirements for each parameter, check out the [given appendix](#appendix-a-acceptable-values-for-parameters).
-Exits the program.
+##### Valid examples
+
+| Command | Reason |
+|----------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|
+| `apply 1 --title SWE`{:.language-sh} | Applies to the **organization** at index 1, for the title of **SWE**. |
+| `apply google --title Unit Tester --by 12-12-2023`{:.language-sh} | Applies to the **organization** with ID of *google** for title of **Unit Tester** by **12-12-2023**. |
+
+##### Invalid examples
+
+| Command | Reason |
+|---------------------------------------|-----------------------------------------------------|
+| `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`{:.language-sh}
+
+
+##### Format
+```sh
+edit --application INDEX [--title TITLE] [--description DESCRIPTION] [--by DEADLINE] [--status STATUS] [--stage STAGE]
+```
+
+Edits the given job application according to the parameters given.
+* You can supply more than one parameter to change multiple details of an application in one command.
+* 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
+
+| Command | Reason |
+|--------------------------------------------|-----------------------------------------------------------------|
+| `edit --application 1 --title SRE`{:.language-sh} | Changes the title of the job application at index 1 to **SRE**. |
+| `edit --application 1 --status pending`{:.language-sh} | Changes the status of job application at index 1 to **pending**. |
+
+##### Invalid examples
+
+| Command | Reason |
+|----------------------------------------------|---------------------------------------|
+| `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 job applications - `delete --application`{:.language-sh}
+
+
+##### Format
+```sh
+delete --application INDEX
+```
+Deletes the job application at the given `INDEX`.
+* 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
+
+| Command | Reason |
+|--------------------------|---------------------------------------------------------------------|
+| `delete --application 1`{:.language-sh} | Deletes the application at index 1. |
+
+##### Invalid examples
+
+| Command | Reason |
+|--------------------------|---------------------------------------------------|
+| `delete --application 0`{:.language-sh} | Invalid index, as index starts from 1. |
+
+
+### Listing data - `list`
+
+
+##### Format
+```sh
+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`{:.language-sh} lists only
. Specifying neither will list all contacts.
+
+* Supplying `--toapply`{:.language-sh} lists
you have not applied to.
+
+##### Valid examples
+
+| Command | Reason |
+|------------------|-------------------------------------------------------------------|
+| `list` | List all **contacts**. |
+| `list --org`{:.language-sh} | Lists all **organization contacts**. |
+| `list --rec`{:.language-sh} | Lists all **recruiter contacts**. |
+| `list --toapply`{:.language-sh} | Lists all **organization contacts** that have not been applied to. |
+
+
+### Searching contacts - `find`
+
+
+##### Format
+```sh
+find KEYWORD...
+```
+
+Finds the contacts whose `NAME` or `ID` contains the given `KEYWORD`.
+* You can supply multiple keywords as long as they are separated by [whitespace](#glossary).
+* If you wish to know more about the requirements for the parameter, check out the [given appendix](#appendix-a-acceptable-values-for-parameters).
+
+##### Valid examples
+
+| Command | Reason |
+|-------------------|-----------------------------------------------------------------------------------------------------|
+| `find jo` | Finds contacts whose `NAME` or `ID` contains the [substring](#glossary) "jo". |
+| `find 1231` | Finds contacts whose `NAME` or `ID` contains the substring "1231". |
+| `find alex david` | Finds contacts whose `NAME` or `ID` contains the substring "alex" or "david". |
+
+##### Rules
+
+
+* The search is case-insensitive. e.g `hans` will match `Hans` and `1231` will match `id_1231`.
+* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`.
+* You can match partial keywords. e.g. searching for `ha` will match with `hamburger`.
+* Contacts matching at least one keyword will be returned (i.e. `OR` search)
+ e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`.
+
+
+### Sorting data - `sort`
+
+
+##### Format
+```sh
+sort --FLAG_TO_SORT [--ascending / --descending]
+```
+
+Sorts contacts or job applications for you by the specified flag.
+`--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
+
+###### Fields for Contacts
+* `--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`{:.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`{:.language-sh} - Will reset the sorting order of
.
-Format: `exit`
+##### Supported secondary parameters
+
+###### Changing the sort 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`{:.language-sh} or `--descending`{:.language-sh} are provided, the list will be sorted in ascending order by default.
+
+* Neither `--ascending`{:.language-sh} nor `--descending`{:.language-sh} may be specified if the flag is `--none`{:.language-sh}.
+
+* Sorting will work even if no
exist. In that case, nothing will happen.
+
+##### Sample demonstration
+* To order your
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
+
+##### Valid examples
+
+| Command | Reason |
+|-----------------------------|------------------------------------------------------------------------------------------------------------|
+| `sort --title --ascending`{:.language-sh} | Sorts **job applications** by title, in ascending alphabetical order. |
+| `sort --url`{:.language-sh} | Sorts **contacts** by url, in the default order - ascending alphabetical. |
+| `sort --stale --descending`{:.language-sh} | Sorts **job applications** by last updated time, in reverse chronological order, from most recent to least. |
+| `sort --none`{:.language-sh} | Resets the sorting order of **contacts** and **job applications**. |
+
+##### Invalid examples
+
+| Command | Reason |
+|----------------------------|---------------------------------------------|
+| `sort` | No field provided. |
+| `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`
+
+
+##### Format
+```sh
+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`{:.language-sh}.
+
+![Remind Earliest](images/starter-guide/remind-earliest.jpg)
+
+##### Valid examples
+
+| Command | Reason |
+|---------------------|--------------------------------------------------------------------------------------|
+| `remind --earliest`{:.language-sh} | Lists the application deadlines in order of urgency, from earliest to latest. |
+| `remind --latest`{:.language-sh} | Lists the application deadlines in order of reverse urgency, from latest to earliest. |
+
+##### Invalid examples
+
+| Command | Reason |
+|---------------------------------------|-----------------------------------------------------|
+| `remind` | No urgency level specified. |
+
+
+### Viewing help - `help`
+
+
+
+##### Format
+```sh
+help
+```
+
+Shows a message explaining how to access the help page.
+
+![Help Message](images/helpMessage.png)
+
+
+### Clearing all data - `clear`
+
+
+##### Format
+```sh
+exit
+```
+
+Exits the program.
### Saving the data
-AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
+Jobby's data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
### Editing the data file
-AddressBook data are saved automatically as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file.
+Jobby's data are saved automatically as a JSON file `[JAR file location]/data/jobby.json`. Advanced users are welcome to update data directly by editing that data file.
-
:exclamation: **Caution:**
-If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
+
+:warning: **Caution:** If your changes to the data file makes its format invalid, Jobby will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
-### Archiving data files `[coming in v2.0]`
+--------------------------------------------------------------------------------------------------------------------
+
+## Command Summary
-_Details coming soon ..._
+### Commands for Handling Contacts
+
+| Action | Format, Examples |
+|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **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]`{:.language-sh} |
+| **Sort Contacts** | `sort --address / --email / --id / --name / --phone / --url [--ascending / --descending]`{:.language-sh} |
+
+### Commands for Handling Job Applications
+
+
+
+
+| Action | Format, Examples |
+|------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **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
+
+| Action | Format, Examples |
+|-----------|------------------|
+| **Clear** | `clear` |
+| **Help** | `help` |
+| **Exit** | `exit` |
+
+
--------------------------------------------------------------------------------------------------------------------
-## FAQ
+## Glossary
+
+| 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 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
+
+### Appendix A: Acceptable values for parameters
+
+| 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 characters and basic symbols (i.e. `a-z`, `A-Z`, `0-9`, `-`, `_`). However it cannot have consecutive underscores and dashes. | `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` |
-**Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder.
--------------------------------------------------------------------------------------------------------------------
-## Known issues
+## Frequently Asked Questions
+
+##### _How do I transfer my data to another device?_
+* Jobby currently does not directly support data transfer.
+ You can transfer your contact data and job application data by copying the data folder in your old _jobby.jar_ home folder to the new home folder for _jobby.jar_.
+
+##### _I want to try out Jobby with some sample data. How can I do so?_
+* You can delete the data folder in the home folder of _jobby.jar_, and launch Jobby again.
+ There will be sample data generated on launch.
+ * Alternatively, you can move the data folder somewhere else if you still want to keep the data.
-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.
+##### _I am currently facing issues with Jobby._
+* We would like to hear the details of the issues that you are having.
+ You can report them through our [bug tracker](https://github.com/AY2324S1-CS2103T-W08-3/tp/issues).
+
+##### _I would like to suggest a new feature for Jobby._
+* We are always looking for suggestions to improve Jobby!
+ You can suggest a new feature to us via the [issue tracker](https://github.com/AY2324S1-CS2103T-W08-3/tp/issues).
--------------------------------------------------------------------------------------------------------------------
+## Issues
+
+1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the _preferences.json_ file created by the application before running the application again.
+
+2. **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.
-## Command summary
-
-Action | Format, Examples
---------|------------------
-**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague`
-**Clear** | `clear`
-**Delete** | `delete INDEX`
e.g., `delete 3`
-**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
e.g.,`edit 2 n/James Lee e/jameslee@example.com`
-**Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake`
-**List** | `list`
-**Help** | `help`
+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.
diff --git a/docs/_config.yml b/docs/_config.yml
index 6bd245d8f4e..74ac03cf341 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,4 +1,4 @@
-title: "AB-3"
+title: "Jobby"
theme: minima
header_pages:
@@ -8,7 +8,10 @@ header_pages:
markdown: kramdown
-repository: "se-edu/addressbook-level3"
+kramdown:
+ toc_levels: "2,3,4"
+
+repository: "ay2324s1-cs2103t-w08-3/tp"
github_icon: "images/github-icon.png"
plugins:
diff --git a/docs/_data/projects.yml b/docs/_data/projects.yml
index 8f3e50cb601..f1aee680705 100644
--- a/docs/_data/projects.yml
+++ b/docs/_data/projects.yml
@@ -1,23 +1,4 @@
-- name: "AB-1"
- url: https://se-edu.github.io/addressbook-level1
+- name: "Jobby"
+ url: https://ay2324s1-cs2103t-w08-3.github.io/tp/
-- name: "AB-2"
- url: https://se-edu.github.io/addressbook-level2
-- name: "AB-3"
- url: https://se-edu.github.io/addressbook-level3
-
-- name: "AB-4"
- url: https://se-edu.github.io/addressbook-level4
-
-- name: "Duke"
- url: https://se-edu.github.io/duke
-
-- name: "Collate"
- url: https://se-edu.github.io/collate
-
-- name: "Book"
- url: https://se-edu.github.io/se-book
-
-- name: "Resources"
- url: https://se-edu.github.io/resources
diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss
index 0d3f6e80ced..e4e06cb2a9b 100644
--- a/docs/_sass/minima/_base.scss
+++ b/docs/_sass/minima/_base.scss
@@ -179,6 +179,8 @@ pre {
border: 0;
padding-right: 0;
padding-left: 0;
+
+ white-space: pre-wrap;
}
}
@@ -203,17 +205,17 @@ pre {
margin-left: auto;
padding-right: $spacing-unit / 2;
padding-left: $spacing-unit / 2;
+ background-color: white;
@extend %clearfix;
@media screen and (min-width: $on-large) {
- max-width: calc(#{$content-width} - (#{$spacing-unit} * 2));
- padding-right: $spacing-unit;
- padding-left: $spacing-unit;
+ max-width: calc(#{$content-width} - (#{$spacing-unit} * 3));
+ padding-right: $spacing-unit * 1.5;
+ padding-left: $spacing-unit * 1.5;
}
}
-
/**
* Clearfix
*/
@@ -288,7 +290,7 @@ table {
text-align: center;
}
.site-header:before {
- content: "AB-3";
+ content: "Jobby";
font-size: 32px;
}
}
diff --git a/docs/_sass/minima/_layout.scss b/docs/_sass/minima/_layout.scss
index ca99f981701..7e3ffb77e6c 100644
--- a/docs/_sass/minima/_layout.scss
+++ b/docs/_sass/minima/_layout.scss
@@ -126,6 +126,26 @@
flex: 1 0 auto;
}
+@media screen and (min-width: $on-large) {
+ /* Paper styling on larger screens */
+ .page-content {
+ background: #fafafa;
+
+ & > .wrapper {
+ padding-top: 48px;
+ padding-bottom: 48px;
+ background-color: white;
+ box-shadow: $paper-box-shadow;
+ }
+ }
+
+ .site-header {
+ border-bottom: none;
+ background-color: white;
+ box-shadow: $paper-box-shadow;
+ }
+}
+
.page-heading {
@include relative-font-size(2);
}
@@ -176,8 +196,41 @@
.post-content {
margin-bottom: $spacing-unit;
- h1, h2, h3 { margin-top: $spacing-unit * 2 }
- h4, h5, h6 { margin-top: $spacing-unit }
+ h1, h2, h3 {
+ margin-top: $spacing-unit * 2.25;
+ margin-bottom: $spacing-unit * 0.5;
+ font-weight: 600;
+ }
+
+ h4 {
+ margin-top: $spacing-unit * 1.75;
+ margin-bottom: $spacing-unit * 0.25;
+ font-weight: 500;
+ }
+
+ h5 {
+ margin-top: $spacing-unit * 1.25;
+ margin-bottom: $spacing-unit * 0.2;
+ font-weight: 500;
+ font-style: italic;
+ }
+
+ h6 {
+ margin-top: $spacing-unit * 1.2;
+ margin-bottom: $spacing-unit * 0.1;
+ font-weight: 700;
+ font-style: italic;
+ }
+
+ h1, h2, h3, h4, h5, h6 {
+ code {
+ font-weight: 400;
+ }
+ }
+
+ h2 + h3, h3 + h4, h4 + h5, h5 + h6 {
+ margin-top: $spacing-unit * 0.5 !important;
+ }
h2 {
@include relative-font-size(1.75);
@@ -202,6 +255,7 @@
h5 {
@include relative-font-size(1.125);
}
+
h6 {
@include relative-font-size(1.0625);
}
@@ -261,3 +315,63 @@
width: calc(50% - (#{$spacing-unit} / 2));
}
}
+
+
+/**
+ * Advanced print formatting
+ */
+@media print {
+ @page {
+ margin: 1.5cm;
+ }
+
+ body {
+ zoom: $print-scale;
+ }
+
+ h2:not(:first-child), h3:not(:first-child):not(
+ h2 + h3,
+ h2 + p + h3,
+ h2 + p + p + h3,
+ h2 + p + p + p + h3,
+ h2 + p + p + p + p + h3,
+ h2 + p + p + p + p + p + h3,
+ h2 + .h2-summary + h3
+ ) {
+ page-break-before: always;
+ }
+
+ h1, h2, h3, h4, h5, h6 {
+ page-break-after: avoid;
+ }
+
+ img, code, .alert, .pill, .jobby-data-class {
+ page-break-inside: avoid;
+ }
+
+ ul ul, ul ol, ol ul, ol ol {
+ page-break-before: avoid;
+ page-break-inside: avoid;
+ }
+
+ .reset-page-break-defaults, .h2-summary {
+ &, & * {
+ page-break-before: auto !important;
+ page-break-inside: auto !important;
+ page-break-after: auto !important;
+ }
+ }
+
+ .page-break, .page-break-after {
+ page-break-after: always !important;
+ }
+
+ .page-break-before {
+ page-break-before: always !important;
+ }
+
+ tr {
+ page-break-inside: avoid;
+ }
+
+}
diff --git a/docs/_sass/minima/custom-styles.scss b/docs/_sass/minima/custom-styles.scss
index 56b5d56b430..1da9573ec20 100644
--- a/docs/_sass/minima/custom-styles.scss
+++ b/docs/_sass/minima/custom-styles.scss
@@ -1,8 +1,15 @@
// Placeholder to allow defining custom styles that override everything else.
// (Use `_sass/minima/custom-variables.scss` to override variable defaults)
-h2, h3, h4, h5, h6 {
+h2, h3, h4 {
color: #e46c0a;
}
+h5 {
+ color: #a94325;
+}
+h6 {
+ color: $text-color;
+}
+
// Bootstrap style alerts
.alert {
@@ -32,3 +39,142 @@ h2, h3, h4, h5, h6 {
}
}
+// Custom basic overrides
+.icon {
+ height: 21px;
+ width: 21px;
+}
+
+hr {
+ opacity: 0.3;
+}
+
+img.emoji {
+ height: 16px;
+ width: 16px;
+ vertical-align: baseline;
+}
+
+// Syntax highlighting term overrides
+// - Multiline
+.language-sh, .language-bash, .language-shell {
+ .highlight code {
+ .nb {
+ /* parsing of this seems inconsistent */
+ color: unset;
+ }
+ .nt {
+ /* do not wrap --flags */
+ white-space: nowrap;
+ }
+ }
+}
+// - Inline
+code.language-plaintext, code.language-sh, code.language-bash, code.language-shell {
+ .nt {
+ /* do not wrap --flags */
+ white-space: nowrap;
+ }
+}
+
+// Pill styles
+.pill {
+
+ border: 1px solid transparent;
+ border-radius: 16px;
+ padding: 2px 12px;
+ margin: 0 2px;
+
+ background-color: #88888822;
+
+ font-family: verdana;
+ font-size: 75%;
+ vertical-align: text-bottom;
+
+ display: inline-block;
+
+ .pill {
+ /* so nesting pills will look the same */
+ font-size: 100%;
+ vertical-align: baseline;
+ }
+
+ img.emoji {
+ height: 12px;
+ width: 12px;
+ vertical-align: unset;
+ margin: -2px 0;
+ }
+
+ &.beginner {
+ background-color: #00cc0022;
+ border-color: #00cc00;
+ color: #008800;
+ }
+
+ &.intermediate {
+ background-color: #ffa50022;
+ border-color: #cc8800;
+ color: #aa5500;
+ }
+
+ &.expert {
+ background-color: #ff000022;
+ border-color: #cc0000;
+ color: #cc0000;
+ }
+
+ &.learning-outcome {
+ background-color: #ffeb99;
+ color: #552800;
+ }
+
+ &.information {
+ background-color: #33aaff22;
+ color: #3377aa;
+ }
+
+ &.danger {
+ background-color: #cc3333;
+ color: #ffffff;
+ }
+
+ &.warning {
+ background-color: #fdcc99;
+ color: #331800;
+ }
+
+ &.applies-to {
+ background-color: #fdfbf9;
+ border-color: #e8e6e5;
+ color: #444444;
+
+ padding-right: 2px;
+ margin: 4px 2px;
+
+ &.jobby-data-class:not(:first-child) {
+ margin-left: 0;
+ }
+
+ &::before {
+ content: "Applies to ";
+ }
+ }
+
+ &.jobby-data-class {
+ border-color: #a8583333;
+ }
+}
+
+// Jobby data class highlighting
+.jobby-data-class {
+ background-color: #ff886633;
+ color: #633412;
+
+ &:not(.pill) {
+ border-radius: 4px;
+ padding: 0 4px;
+ }
+}
+
+
diff --git a/docs/_sass/minima/initialize.scss b/docs/_sass/minima/initialize.scss
index 30288811151..17f703a96d2 100644
--- a/docs/_sass/minima/initialize.scss
+++ b/docs/_sass/minima/initialize.scss
@@ -13,15 +13,20 @@ $spacing-unit: 30px !default;
$table-text-align: left !default;
+$print-scale: 0.8 !default;
+
// Width of the content area
-$content-width: 800px !default;
+$content-width: 1024px !default;
$on-palm: 600px !default;
-$on-laptop: 800px !default;
+$on-laptop: 840px !default;
$on-medium: $on-palm !default;
$on-large: $on-laptop !default;
+// Paper styling on large screens
+$paper-box-shadow: 0 2px 6px 1px #00000018;
+
// Use media queries like this:
// @include media-query($on-palm) {
// .wrapper {
diff --git a/docs/assets/css/style.scss b/docs/assets/css/style.scss
index b5ec6976efa..cbe4bf58b7c 100644
--- a/docs/assets/css/style.scss
+++ b/docs/assets/css/style.scss
@@ -6,7 +6,4 @@
"minima/skins/{{ site.minima.skin | default: 'classic' }}",
"minima/initialize";
-.icon {
- height: 21px;
- width: 21px
-}
+
diff --git a/docs/diagrams/AddRecruiterActivityDiagram.puml b/docs/diagrams/AddRecruiterActivityDiagram.puml
new file mode 100644
index 00000000000..08487b07961
--- /dev/null
+++ b/docs/diagrams/AddRecruiterActivityDiagram.puml
@@ -0,0 +1,27 @@
+@startuml
+skin rose
+skinparam ActivityFontSize 15
+skinparam ArrowFontSize 12
+start
+
+:AddRecruiterCommand is executed;
+
+if () then ([oid value was passed in])
+
+ if () then ([id belongs to an organization])
+ :Create new Recruiter with parent;
+ else ([else])
+ :throw CommandException;
+ stop
+ endif
+
+else ([else])
+ :Create new Recruiter with no parent;
+endif
+
+:Add Recruiter to AddressBook;
+:return CommandResult;
+
+stop
+
+@enduml
diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml
index 48b6cc4333c..b5c4b8cce84 100644
--- a/docs/diagrams/ArchitectureSequenceDiagram.puml
+++ b/docs/diagrams/ArchitectureSequenceDiagram.puml
@@ -14,7 +14,7 @@ activate ui UI_COLOR
ui -[UI_COLOR]> logic : execute("delete 1")
activate logic LOGIC_COLOR
-logic -[LOGIC_COLOR]> model : deletePerson(p)
+logic -[LOGIC_COLOR]> model : deleteContact(p)
activate model MODEL_COLOR
model -[MODEL_COLOR]-> logic
diff --git a/docs/diagrams/AutocompleteClasses.puml b/docs/diagrams/AutocompleteClasses.puml
new file mode 100644
index 00000000000..dfed450f38b
--- /dev/null
+++ b/docs/diagrams/AutocompleteClasses.puml
@@ -0,0 +1,61 @@
+@startuml
+!include style.puml
+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
+}
+
+'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
+
+ class "AutocompleteItemSet" as AutocompleteItemSet
+ class "<>\nAutocompleteConstraint" as AutocompleteConstraint
+
+ class "<>\nFlagValueSupplier" as FlagValueSupplier
+ class PartitionedCommand
+}
+
+package Model as ModelPackage {
+}
+package "Parser classes" <> {
+ class Flag
+}
+
+'relationships
+HiddenOutside1 .down.> AutocompleteGenerator
+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.> ModelPackage : uses >
+PartitionedCommand ..down.> Flag
+@enduml
diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml
index 598474a5c82..ba3edcffabe 100644
--- a/docs/diagrams/BetterModelClassDiagram.puml
+++ b/docs/diagrams/BetterModelClassDiagram.puml
@@ -4,18 +4,17 @@ skinparam arrowThickness 1.1
skinparam arrowColor MODEL_COLOR
skinparam classBackgroundColor MODEL_COLOR
-AddressBook *-right-> "1" UniquePersonList
+AddressBook *-right-> "1" UniqueContactList
AddressBook *-right-> "1" UniqueTagList
-UniqueTagList -[hidden]down- UniquePersonList
-UniqueTagList -[hidden]down- UniquePersonList
+UniqueTagList -[hidden]down- UniqueContactList
+UniqueTagList -[hidden]down- UniqueContactList
UniqueTagList -right-> "*" Tag
-UniquePersonList -right-> Person
+UniqueContactList -right-> Contact
-Person -up-> "*" Tag
+Contact -up-> "*" Tag
+
+Organization -up-|> Contact
+Recruiter -up-|> Contact
-Person *--> Name
-Person *--> Phone
-Person *--> Email
-Person *--> Address
@enduml
diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml
index 40ea6c9dc4c..bc0fa839b53 100644
--- a/docs/diagrams/DeleteSequenceDiagram.puml
+++ b/docs/diagrams/DeleteSequenceDiagram.puml
@@ -49,7 +49,7 @@ deactivate AddressBookParser
LogicManager -> DeleteCommand : execute()
activate DeleteCommand
-DeleteCommand -> Model : deletePerson(1)
+DeleteCommand -> Model : deleteContact(1)
activate Model
Model --> DeleteCommand
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/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml
index 0de5673070d..5ec24fc0fdd 100644
--- a/docs/diagrams/ModelClassDiagram.puml
+++ b/docs/diagrams/ModelClassDiagram.puml
@@ -12,13 +12,13 @@ Class AddressBook
Class ModelManager
Class UserPrefs
-Class UniquePersonList
-Class Person
-Class Address
-Class Email
-Class Name
-Class Phone
-Class Tag
+Class UniqueContactList
+Class "{abstract}\nContact" as Contact
+Class Organization
+Class Recruiter
+
+Class JobApplicationList
+Class JobApplication
Class I #FFFFFF
}
@@ -35,20 +35,16 @@ ModelManager -left-> "1" AddressBook
ModelManager -right-> "1" UserPrefs
UserPrefs .up.|> ReadOnlyUserPrefs
-AddressBook *--> "1" UniquePersonList
-UniquePersonList --> "~* all" Person
-Person *--> Name
-Person *--> Phone
-Person *--> Email
-Person *--> Address
-Person *--> "*" Tag
+AddressBook *--> "1" UniqueContactList
+UniqueContactList --> "~* all" Contact
+
+ModelManager *-down-> "1" JobApplicationList
+JobApplicationList -down-> "*" JobApplication
-Person -[hidden]up--> I
-UniquePersonList -[hidden]right-> I
+Contact -[hidden]up--> I
-Name -[hidden]right-> Phone
-Phone -[hidden]right-> Address
-Address -[hidden]right-> Email
+Organization -up-|> Contact
+Recruiter -up-|> Contact
-ModelManager --> "~* filtered" Person
+ModelManager --> "~* filtered" Contact
@enduml
diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml
index 0c7424de6e0..07f01f1bfbf 100644
--- a/docs/diagrams/ParserClasses.puml
+++ b/docs/diagrams/ParserClasses.puml
@@ -9,30 +9,30 @@ Class XYZCommand
package "Parser classes"{
Class "<>\nParser" as Parser
-Class AddressBookParser
+Class AppParser
Class XYZCommandParser
Class CliSyntax
Class ParserUtil
Class ArgumentMultimap
Class ArgumentTokenizer
-Class Prefix
+Class Flag
}
Class HiddenOutside #FFFFFF
-HiddenOutside ..> AddressBookParser
+HiddenOutside ..> AppParser
-AddressBookParser .down.> XYZCommandParser: creates >
+AppParser .down.> XYZCommandParser: creates >
XYZCommandParser ..> XYZCommand : creates >
-AddressBookParser ..> Command : returns >
+AppParser ..> Command : returns >
XYZCommandParser .up.|> Parser
XYZCommandParser ..> ArgumentMultimap
XYZCommandParser ..> ArgumentTokenizer
ArgumentTokenizer .left.> ArgumentMultimap
XYZCommandParser ..> CliSyntax
-CliSyntax ..> Prefix
+CliSyntax ..> Flag
XYZCommandParser ..> ParserUtil
-ParserUtil .down.> Prefix
-ArgumentTokenizer .down.> Prefix
+ParserUtil .down.> Flag
+ArgumentTokenizer .down.> Flag
XYZCommand -up-|> Command
@enduml
diff --git a/docs/diagrams/SortSequenceDiagram.puml b/docs/diagrams/SortSequenceDiagram.puml
new file mode 100644
index 00000000000..f6a5d7261f0
--- /dev/null
+++ b/docs/diagrams/SortSequenceDiagram.puml
@@ -0,0 +1,69 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":SortCommandParser" as SortCommandParser LOGIC_COLOR
+participant ":SortCommand" as SortCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("sort --status")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("sort --status")
+activate AddressBookParser
+
+create SortCommandParser
+AddressBookParser -> SortCommandParser
+activate SortCommandParser
+
+SortCommandParser --> AddressBookParser
+deactivate SortCommandParser
+
+AddressBookParser -> SortCommandParser : parse("--status")
+activate SortCommandParser
+
+create SortCommand
+SortCommandParser -> SortCommand
+activate SortCommand
+
+SortCommand --> SortCommandParser
+deactivate SortCommand
+
+SortCommandParser --> AddressBookParser
+deactivate SortCommandParser
+SortCommandParser -[hidden]-> AddressBookParser
+destroy SortCommandParser
+
+AddressBookParser --> LogicManager
+deactivate AddressBookParser
+
+LogicManager -> SortCommand : execute()
+activate SortCommand
+
+SortCommand -> Model : updateSortedApplicationList(STATUS_COMPARATOR)
+activate Model
+
+Model --> SortCommand
+deactivate Model
+
+create CommandResult
+SortCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> SortCommand
+deactivate CommandResult
+
+SortCommand --> LogicManager : result
+deactivate SortCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml
index a821e06458c..457b68ee498 100644
--- a/docs/diagrams/StorageClassDiagram.puml
+++ b/docs/diagrams/StorageClassDiagram.puml
@@ -18,7 +18,8 @@ package "AddressBook Storage" #F4F6F6{
Class "<>\nAddressBookStorage" as AddressBookStorage
Class JsonAddressBookStorage
Class JsonSerializableAddressBook
-Class JsonAdaptedPerson
+Class JsonAdaptedContact
+Class JsonAdaptedApplication
Class JsonAdaptedTag
}
@@ -37,7 +38,8 @@ Storage -right-|> AddressBookStorage
JsonUserPrefsStorage .up.|> UserPrefsStorage
JsonAddressBookStorage .up.|> AddressBookStorage
JsonAddressBookStorage ..> JsonSerializableAddressBook
-JsonSerializableAddressBook --> "*" JsonAdaptedPerson
-JsonAdaptedPerson --> "*" JsonAdaptedTag
+JsonSerializableAddressBook --> "*" JsonAdaptedContact
+JsonAdaptedContact --> "*" JsonAdaptedTag
+JsonAdaptedContact --> "*" JsonAdaptedApplication
@enduml
diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml
index 95473d5aa19..73a5e0b8ebd 100644
--- a/docs/diagrams/UiClassDiagram.puml
+++ b/docs/diagrams/UiClassDiagram.puml
@@ -11,10 +11,13 @@ Class UiManager
Class MainWindow
Class HelpWindow
Class ResultDisplay
-Class PersonListPanel
-Class PersonCard
+Class ContactListPanel
+Class ContactCard
+Class ApplicationListPanel
+Class ApplicationCard
Class StatusBarFooter
Class CommandBox
+Class AutocompleteTextField
}
package Model <> {
@@ -32,26 +35,34 @@ UiManager .left.|> Ui
UiManager -down-> "1" MainWindow
MainWindow *-down-> "1" CommandBox
MainWindow *-down-> "1" ResultDisplay
-MainWindow *-down-> "1" PersonListPanel
+MainWindow *-down-> "1" ContactListPanel
+MainWindow *-down-> "1" ApplicationListPanel
MainWindow *-down-> "1" StatusBarFooter
MainWindow --> "0..1" HelpWindow
-PersonListPanel -down-> "*" PersonCard
+ContactListPanel -down-> "*" ContactCard
+ApplicationListPanel -down-> "*" ApplicationCard
-MainWindow -left-|> UiPart
+MainWindow --|> UiPart
ResultDisplay --|> UiPart
CommandBox --|> UiPart
-PersonListPanel --|> UiPart
-PersonCard --|> UiPart
+ContactListPanel --|> UiPart
+ContactCard --|> UiPart
StatusBarFooter --|> UiPart
HelpWindow --|> UiPart
+ApplicationListPanel --|> UiPart
+ApplicationCard --|> UiPart
-PersonCard ..> Model
+CommandBox -down--> "1" AutocompleteTextField
+
+ContactCard ..> Model
+ApplicationCard ..> Model
UiManager -right-> Logic
MainWindow -left-> Logic
-PersonListPanel -[hidden]left- HelpWindow
+ContactListPanel -[hidden]left- HelpWindow
+ApplicationListPanel -[hidden]left- ContactListPanel
HelpWindow -[hidden]left- CommandBox
CommandBox -[hidden]left- ResultDisplay
ResultDisplay -[hidden]left- StatusBarFooter
diff --git a/docs/diagrams/apply-command/ApplyCommand.puml b/docs/diagrams/apply-command/ApplyCommand.puml
new file mode 100644
index 00000000000..e156fb81cbb
--- /dev/null
+++ b/docs/diagrams/apply-command/ApplyCommand.puml
@@ -0,0 +1,70 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AppParser" as AddressBookParser LOGIC_COLOR
+participant ":ApplyCommandParser" as ApplyCommandParser LOGIC_COLOR
+participant ":ApplyCommand" as ApplyCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("apply 1 --title SWE")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("apply 1 --title SWE")
+activate AddressBookParser
+
+create ApplyCommandParser
+AddressBookParser -> ApplyCommandParser
+activate ApplyCommandParser
+
+ApplyCommandParser --> AddressBookParser
+deactivate ApplyCommandParser
+
+AddressBookParser -> ApplyCommandParser : parse(" --title SWE")
+activate ApplyCommandParser
+
+create ApplyCommand
+ApplyCommandParser -> ApplyCommand
+activate ApplyCommand
+
+ApplyCommand --> ApplyCommandParser :
+deactivate ApplyCommand
+
+ApplyCommandParser --> AddressBookParser :
+deactivate ApplyCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+ApplyCommandParser -[hidden]-> AddressBookParser
+destroy ApplyCommandParser
+
+AddressBookParser --> LogicManager :
+deactivate AddressBookParser
+
+LogicManager -> ApplyCommand : execute()
+activate ApplyCommand
+
+ApplyCommand -> Model : addJobApplication()
+activate Model
+
+Model --> ApplyCommand
+deactivate Model
+
+create CommandResult
+ApplyCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> ApplyCommand
+deactivate CommandResult
+
+ApplyCommand --> LogicManager : result
+deactivate ApplyCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/apply-command/style.puml b/docs/diagrams/apply-command/style.puml
new file mode 100644
index 00000000000..f7d7347ae84
--- /dev/null
+++ b/docs/diagrams/apply-command/style.puml
@@ -0,0 +1,79 @@
+/'
+ 'Commonly used styles and colors across diagrams.
+ 'Refer to https://plantuml-documentation.readthedocs.io/en/latest for a more
+ 'comprehensive list of skinparams.
+ '/
+
+
+'T1 through T4 are shades of the original color from lightest to darkest
+
+!define UI_COLOR #1D8900
+!define UI_COLOR_T1 #83E769
+!define UI_COLOR_T2 #3FC71B
+!define UI_COLOR_T3 #166800
+!define UI_COLOR_T4 #0E4100
+
+!define LOGIC_COLOR #3333C4
+!define LOGIC_COLOR_T1 #C8C8FA
+!define LOGIC_COLOR_T2 #6A6ADC
+!define LOGIC_COLOR_T3 #1616B0
+!define LOGIC_COLOR_T4 #101086
+
+!define MODEL_COLOR #9D0012
+!define MODEL_COLOR_T1 #F97181
+!define MODEL_COLOR_T2 #E41F36
+!define MODEL_COLOR_T3 #7B000E
+!define MODEL_COLOR_T4 #51000A
+
+!define STORAGE_COLOR #A38300
+!define STORAGE_COLOR_T1 #FFE374
+!define STORAGE_COLOR_T2 #EDC520
+!define STORAGE_COLOR_T3 #806600
+!define STORAGE_COLOR_T2 #544400
+
+!define USER_COLOR #000000
+
+skinparam Package {
+ BackgroundColor #FFFFFF
+ BorderThickness 1
+ FontSize 16
+}
+
+skinparam Class {
+ FontColor #FFFFFF
+ FontSize 15
+ BorderThickness 1
+ BorderColor #FFFFFF
+ StereotypeFontColor #FFFFFF
+ FontName Arial
+}
+
+skinparam Actor {
+ BorderColor USER_COLOR
+ Color USER_COLOR
+ FontName Arial
+}
+
+skinparam Sequence {
+ MessageAlign center
+ BoxFontSize 15
+ BoxPadding 0
+ BoxFontColor #FFFFFF
+ FontName Arial
+}
+
+skinparam Participant {
+ FontColor #FFFFFFF
+ Padding 20
+}
+
+skinparam ArrowFontStyle bold
+skinparam MinClassWidth 50
+skinparam ParticipantPadding 10
+skinparam Shadowing false
+skinparam DefaultTextAlignment center
+skinparam packageStyle Rectangle
+
+hide footbox
+hide members
+hide circle
diff --git a/docs/diagrams/enhancements/FlagChecker.puml b/docs/diagrams/enhancements/FlagChecker.puml
new file mode 100644
index 00000000000..cdd1e0f8df9
--- /dev/null
+++ b/docs/diagrams/enhancements/FlagChecker.puml
@@ -0,0 +1,41 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AppParser" as AddressBookParser LOGIC_COLOR
+participant ":EditCommandParser" as EditCommandParser LOGIC_COLOR
+end box
+
+[-> LogicManager : execute("edit --application 1 --name Jay --title SWE")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("edit --application 1 --name Jay --title SWE")
+activate AddressBookParser
+
+create EditCommandParser
+AddressBookParser -> EditCommandParser
+activate EditCommandParser
+
+EditCommandParser --> AddressBookParser
+deactivate EditCommandParser
+
+AddressBookParser -> EditCommandParser : parse("--application 1 --name Jay --title SWE")
+activate EditCommandParser
+
+EditCommandParser -> EditCommandParser : validateFlags()
+
+EditCommandParser --> AddressBookParser : d
+deactivate EditCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+EditCommandParser -[hidden]-> AddressBookParser
+destroy EditCommandParser
+
+AddressBookParser --> LogicManager : d
+deactivate AddressBookParser
+
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/enhancements/style.puml b/docs/diagrams/enhancements/style.puml
new file mode 100644
index 00000000000..f7d7347ae84
--- /dev/null
+++ b/docs/diagrams/enhancements/style.puml
@@ -0,0 +1,79 @@
+/'
+ 'Commonly used styles and colors across diagrams.
+ 'Refer to https://plantuml-documentation.readthedocs.io/en/latest for a more
+ 'comprehensive list of skinparams.
+ '/
+
+
+'T1 through T4 are shades of the original color from lightest to darkest
+
+!define UI_COLOR #1D8900
+!define UI_COLOR_T1 #83E769
+!define UI_COLOR_T2 #3FC71B
+!define UI_COLOR_T3 #166800
+!define UI_COLOR_T4 #0E4100
+
+!define LOGIC_COLOR #3333C4
+!define LOGIC_COLOR_T1 #C8C8FA
+!define LOGIC_COLOR_T2 #6A6ADC
+!define LOGIC_COLOR_T3 #1616B0
+!define LOGIC_COLOR_T4 #101086
+
+!define MODEL_COLOR #9D0012
+!define MODEL_COLOR_T1 #F97181
+!define MODEL_COLOR_T2 #E41F36
+!define MODEL_COLOR_T3 #7B000E
+!define MODEL_COLOR_T4 #51000A
+
+!define STORAGE_COLOR #A38300
+!define STORAGE_COLOR_T1 #FFE374
+!define STORAGE_COLOR_T2 #EDC520
+!define STORAGE_COLOR_T3 #806600
+!define STORAGE_COLOR_T2 #544400
+
+!define USER_COLOR #000000
+
+skinparam Package {
+ BackgroundColor #FFFFFF
+ BorderThickness 1
+ FontSize 16
+}
+
+skinparam Class {
+ FontColor #FFFFFF
+ FontSize 15
+ BorderThickness 1
+ BorderColor #FFFFFF
+ StereotypeFontColor #FFFFFF
+ FontName Arial
+}
+
+skinparam Actor {
+ BorderColor USER_COLOR
+ Color USER_COLOR
+ FontName Arial
+}
+
+skinparam Sequence {
+ MessageAlign center
+ BoxFontSize 15
+ BoxPadding 0
+ BoxFontColor #FFFFFF
+ FontName Arial
+}
+
+skinparam Participant {
+ FontColor #FFFFFFF
+ Padding 20
+}
+
+skinparam ArrowFontStyle bold
+skinparam MinClassWidth 50
+skinparam ParticipantPadding 10
+skinparam Shadowing false
+skinparam DefaultTextAlignment center
+skinparam packageStyle Rectangle
+
+hide footbox
+hide members
+hide circle
diff --git a/docs/diagrams/enhancements/warn.puml b/docs/diagrams/enhancements/warn.puml
new file mode 100644
index 00000000000..3ed12b2a179
--- /dev/null
+++ b/docs/diagrams/enhancements/warn.puml
@@ -0,0 +1,54 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AppParser" as AddressBookParser LOGIC_COLOR
+participant ":ClearCommand" as ClearCommand LOGIC_COLOR
+participant ":WarnMessageGenerator" as WarnCommand LOGIC_COLOR
+end box
+
+[-> LogicManager : execute("clear")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("clear")
+activate AddressBookParser
+
+create ClearCommand
+AddressBookParser -> ClearCommand
+activate ClearCommand
+
+ClearCommand --> AddressBookParser
+deactivate ClearCommand
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+ClearCommand -[hidden]-> AddressBookParser
+
+AddressBookParser --> LogicManager
+deactivate AddressBookParser
+
+LogicManager -> WarnCommand : generateWarningMessage()
+activate WarnCommand
+
+WarnCommand --> LogicManager : warningMessage
+
+deactivate WarnCommand
+
+
+[<--LogicManager
+deactivate LogicManager
+
+[-> LogicManager : execute("yes")
+activate LogicManager
+
+LogicManager -> ClearCommand : execute()
+activate ClearCommand
+ClearCommand --> LogicManager : result
+deactivate ClearCommand
+ClearCommand -[hidden]-> LogicManager
+[<-- LogicManager
+deactivate LogicManager
+
+
+destroy ClearCommand
+@enduml
diff --git a/docs/diagrams/enhancements/warn_alt.puml b/docs/diagrams/enhancements/warn_alt.puml
new file mode 100644
index 00000000000..ba346d0c169
--- /dev/null
+++ b/docs/diagrams/enhancements/warn_alt.puml
@@ -0,0 +1,52 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AppParser" as AddressBookParser LOGIC_COLOR
+participant ":ClearCommand" as ClearCommand LOGIC_COLOR
+end box
+
+[-> LogicManager : execute("clear")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("clear")
+activate AddressBookParser
+
+create ClearCommand
+AddressBookParser -> ClearCommand
+activate ClearCommand
+
+ClearCommand --> AddressBookParser
+deactivate ClearCommand
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+ClearCommand -[hidden]-> AddressBookParser
+
+AddressBookParser -> AddressBookParser : generateWarningCommand()
+
+AddressBookParser --> LogicManager : warningMessage
+deactivate AddressBookParser
+
+[<--LogicManager
+deactivate LogicManager
+
+[-> LogicManager : execute("yes")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("yes")
+activate AddressBookParser
+AddressBookParser --> LogicManager : clearCommand
+deactivate AddressBookParser
+
+LogicManager -> ClearCommand : execute()
+activate ClearCommand
+ClearCommand --> LogicManager : result
+deactivate ClearCommand
+ClearCommand -[hidden]-> LogicManager
+[<-- LogicManager
+deactivate LogicManager
+
+
+destroy ClearCommand
+@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/docs/diagrams/tracing/LogicSequenceDiagram.puml b/docs/diagrams/tracing/LogicSequenceDiagram.puml
index 42bf46d3ce8..5eb54978456 100644
--- a/docs/diagrams/tracing/LogicSequenceDiagram.puml
+++ b/docs/diagrams/tracing/LogicSequenceDiagram.puml
@@ -14,7 +14,7 @@ create ecp
abp -> ecp
abp -> ecp ++: parse(arguments)
create ec
-ecp -> ec ++: index, editPersonDescriptor
+ecp -> ec ++: index, editContactDescriptor
ec --> ecp --
ecp --> abp --: command
abp --> logic --: command
diff --git a/docs/images/AddRecruiterActivityDiagram.png b/docs/images/AddRecruiterActivityDiagram.png
new file mode 100644
index 00000000000..f514f85e064
Binary files /dev/null and b/docs/images/AddRecruiterActivityDiagram.png differ
diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png
index 37ad06a2803..50b47739fbc 100644
Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ
diff --git a/docs/images/AutocompleteClasses.png b/docs/images/AutocompleteClasses.png
new file mode 100644
index 00000000000..28787acc40c
Binary files /dev/null and b/docs/images/AutocompleteClasses.png differ
diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png
index 02a42e35e76..13ebd9859ee 100644
Binary files a/docs/images/BetterModelClassDiagram.png and b/docs/images/BetterModelClassDiagram.png differ
diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png
index e3b784310fe..d27e6627eb1 100644
Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ
diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png
index a19fb1b4ac8..f0e24cd9e4e 100644
Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ
diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png
index edfd1ff7897..4cdb2ed4dd6 100644
Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ
diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png
index 18fa4d0d51f..c05d66160e4 100644
Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
index 5bd77847aa2..88695b6421b 100644
Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ
diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png
index 11f06d68671..9dd8e2514f3 100644
Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ
diff --git a/docs/images/add_recruiter_success.png b/docs/images/add_recruiter_success.png
new file mode 100644
index 00000000000..85cea932692
Binary files /dev/null and b/docs/images/add_recruiter_success.png differ
diff --git a/docs/images/apply-command/ApplyCommand.png b/docs/images/apply-command/ApplyCommand.png
new file mode 100644
index 00000000000..42c3bf1131b
Binary files /dev/null and b/docs/images/apply-command/ApplyCommand.png differ
diff --git a/docs/images/autocomplete.png b/docs/images/autocomplete.png
new file mode 100644
index 00000000000..8484abc1b65
Binary files /dev/null and b/docs/images/autocomplete.png differ
diff --git a/docs/images/cj-lee01.png b/docs/images/cj-lee01.png
new file mode 100644
index 00000000000..9ee64c59990
Binary files /dev/null and b/docs/images/cj-lee01.png differ
diff --git a/docs/images/enhancements/FlagChecker.png b/docs/images/enhancements/FlagChecker.png
new file mode 100644
index 00000000000..def4f89cf7e
Binary files /dev/null and b/docs/images/enhancements/FlagChecker.png differ
diff --git a/docs/images/enhancements/formatting.png b/docs/images/enhancements/formatting.png
new file mode 100644
index 00000000000..6b2e7335f8c
Binary files /dev/null and b/docs/images/enhancements/formatting.png differ
diff --git a/docs/images/enhancements/warn.png b/docs/images/enhancements/warn.png
new file mode 100644
index 00000000000..830647c8ce7
Binary files /dev/null and b/docs/images/enhancements/warn.png differ
diff --git a/docs/images/enhancements/warn_alt.png b/docs/images/enhancements/warn_alt.png
new file mode 100644
index 00000000000..fbfea03c2f1
Binary files /dev/null and b/docs/images/enhancements/warn_alt.png differ
diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png
index b1f70470137..8ff69f0812b 100644
Binary files a/docs/images/helpMessage.png and b/docs/images/helpMessage.png differ
diff --git a/docs/images/mcnabry.png b/docs/images/mcnabry.png
new file mode 100644
index 00000000000..7cecb180ef8
Binary files /dev/null and b/docs/images/mcnabry.png differ
diff --git a/docs/images/sort-command/SortCommand.png b/docs/images/sort-command/SortCommand.png
new file mode 100644
index 00000000000..0585ce95f85
Binary files /dev/null and b/docs/images/sort-command/SortCommand.png differ
diff --git a/docs/images/sort_deadline.png b/docs/images/sort_deadline.png
new file mode 100644
index 00000000000..1876f919282
Binary files /dev/null and b/docs/images/sort_deadline.png differ
diff --git a/docs/images/starter-guide/add-application.jpg b/docs/images/starter-guide/add-application.jpg
new file mode 100644
index 00000000000..45f2b5629e2
Binary files /dev/null and b/docs/images/starter-guide/add-application.jpg differ
diff --git a/docs/images/starter-guide/add-john.jpg b/docs/images/starter-guide/add-john.jpg
new file mode 100644
index 00000000000..7705e50ea9b
Binary files /dev/null and b/docs/images/starter-guide/add-john.jpg differ
diff --git a/docs/images/starter-guide/add-woogle.jpg b/docs/images/starter-guide/add-woogle.jpg
new file mode 100644
index 00000000000..d0270036e4f
Binary files /dev/null and b/docs/images/starter-guide/add-woogle.jpg differ
diff --git a/docs/images/starter-guide/delete-recursive.jpg b/docs/images/starter-guide/delete-recursive.jpg
new file mode 100644
index 00000000000..f42b96512a3
Binary files /dev/null and b/docs/images/starter-guide/delete-recursive.jpg differ
diff --git a/docs/images/starter-guide/edit woogle.jpg b/docs/images/starter-guide/edit woogle.jpg
new file mode 100644
index 00000000000..7039ce95f5b
Binary files /dev/null and b/docs/images/starter-guide/edit woogle.jpg differ
diff --git a/docs/images/starter-guide/edit-application.jpg b/docs/images/starter-guide/edit-application.jpg
new file mode 100644
index 00000000000..9ec1306f6fe
Binary files /dev/null and b/docs/images/starter-guide/edit-application.jpg differ
diff --git a/docs/images/starter-guide/edit-john.jpg b/docs/images/starter-guide/edit-john.jpg
new file mode 100644
index 00000000000..6c8f093ee93
Binary files /dev/null and b/docs/images/starter-guide/edit-john.jpg differ
diff --git a/docs/images/starter-guide/find-woogle.jpg b/docs/images/starter-guide/find-woogle.jpg
new file mode 100644
index 00000000000..ba533eaf8c2
Binary files /dev/null and b/docs/images/starter-guide/find-woogle.jpg differ
diff --git a/docs/images/starter-guide/initial-ui.jpg b/docs/images/starter-guide/initial-ui.jpg
new file mode 100644
index 00000000000..4742a31eabe
Binary files /dev/null and b/docs/images/starter-guide/initial-ui.jpg differ
diff --git a/docs/images/starter-guide/list.jpg b/docs/images/starter-guide/list.jpg
new file mode 100644
index 00000000000..1aa530deb23
Binary files /dev/null and b/docs/images/starter-guide/list.jpg differ
diff --git a/docs/images/starter-guide/remind-earliest.jpg b/docs/images/starter-guide/remind-earliest.jpg
new file mode 100644
index 00000000000..c92f8611d74
Binary files /dev/null and b/docs/images/starter-guide/remind-earliest.jpg differ
diff --git a/docs/images/tanshiyu1999.png b/docs/images/tanshiyu1999.png
new file mode 100644
index 00000000000..894f9e6b6b5
Binary files /dev/null and b/docs/images/tanshiyu1999.png differ
diff --git a/docs/images/ug-images/app-added.png b/docs/images/ug-images/app-added.png
new file mode 100644
index 00000000000..7b47ca335d8
Binary files /dev/null and b/docs/images/ug-images/app-added.png differ
diff --git a/docs/images/ug-images/labelled-gui.png b/docs/images/ug-images/labelled-gui.png
new file mode 100644
index 00000000000..54cf7cf5d25
Binary files /dev/null and b/docs/images/ug-images/labelled-gui.png differ
diff --git a/docs/images/ug-images/org-added.png b/docs/images/ug-images/org-added.png
new file mode 100644
index 00000000000..d52d4700d8c
Binary files /dev/null and b/docs/images/ug-images/org-added.png differ
diff --git a/docs/images/ug-images/rec-added.png b/docs/images/ug-images/rec-added.png
new file mode 100644
index 00000000000..1a5076c11c8
Binary files /dev/null and b/docs/images/ug-images/rec-added.png differ
diff --git a/docs/images/wamps-jp.png b/docs/images/wamps-jp.png
new file mode 100644
index 00000000000..f43fb34dc46
Binary files /dev/null and b/docs/images/wamps-jp.png differ
diff --git a/docs/images/johndoe.png b/docs/images/wxwern.png
similarity index 100%
rename from docs/images/johndoe.png
rename to docs/images/wxwern.png
diff --git a/docs/index.md b/docs/index.md
index 7601dbaad0d..16bbf079408 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,19 +1,23 @@
---
layout: page
-title: AddressBook Level-3
+title: Jobby
---
-[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions)
-[![codecov](https://codecov.io/gh/se-edu/addressbook-level3/branch/master/graph/badge.svg)](https://codecov.io/gh/se-edu/addressbook-level3)
+
-![Ui](images/Ui.png)
+
+
+
+
-**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface).
+
-* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
-* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
+**Jobby is a desktop application for managing your job application details, specifically organization and recruiter contacts, plus application info and status.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface).
+
+* If you are interested in using Jobby, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
+* If you are interested about developing Jobby, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
**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).
diff --git a/docs/team/cj-lee01.md b/docs/team/cj-lee01.md
new file mode 100644
index 00000000000..6db280b94d9
--- /dev/null
+++ b/docs/team/cj-lee01.md
@@ -0,0 +1,62 @@
+---
+layout: page
+title: Chun Jie's Project Portfolio Page
+---
+
+
+
+### Project: Jobby
+
+Jobby is a desktop address book and job application tracking tool. The user interacts with it using a CLI, and it has a GUI created in JavaFX. It is written in Java.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Apply command
+ * What it does: Allows users to add job applications.
+ * Justification: For job application tracking, since one person can apply to multiple positions within a company, a separate class is required for job applications.
+ * Highlights: Created necessary classes for storing information related to job applications.
+
+* **New Feature**: UI for showing job applications
+ * What it does: Allows users to view their job applications.
+ * Justification: To let them see what job applications they have added.
+
+* **New Feature**: Storage of job applications
+ * What it does: Stores information on job applications in JSON file.
+ * Justification: For users to save their job applications.
+ * Highlights: Decided to store the job applications instead the organization JSON to ensure that the job application is truly associated to it.
+
+* **New Feature**: Editing of job applications
+ * What it does: Allows users to edit job applications.
+ * Justification: In case their job application status changes or they made a mistake while adding the job application.
+
+* **New Feature**: Deleting of job applications
+ * What it does: Allows users to delete job applications.
+ * Justification: Allows users to delete old job applications.
+
+
+* **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=CJ-Lee01&tabRepo=AY2324S1-CS2103T-W08-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~other~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+* **Project management**:
+ * Identify and allocate work among team: E.g. sections of UG, DG, bugs to fix
+
+* **Enhancements to existing features**: Delete recursive
+ * Allows users to delete recruiters linked to an organization when deleting the organization.
+ * This is due to the parent-child relationship between recruiters and organization.
+
+
+* **Documentation**:
+ * User Guide:
+ * Apply, Edit applications and delete command.
+ * Enhancements to Introduction section and Navigating the Guide section.
+ * Developer Guide:
+ * Implementation details for apply command, added planned enhancements
+ * Add more user stories
+ * Add more use cases
+ * Add some non-functional requirements
+ * Updating manual testing guide
+
+* **Community**:
+ * Contributed to forum discussions (examples: #371)
+ * Reviewed PRs: #39, #42, #60, #67, #99, #147, #150,
+
+
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index 773a07794e2..00000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,46 +0,0 @@
----
-layout: page
-title: John Doe's Project Portfolio Page
----
-
-### Project: AddressBook Level 3
-
-AddressBook - Level 3 is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
-
-Given below are my contributions to the project.
-
-* **New Feature**: Added the ability to undo/redo previous commands.
- * What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command.
- * Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them.
- * Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands.
- * Credits: *{mention here if you reused any code/ideas from elsewhere or if a third-party library is heavily used in the feature so that a reader can make a more accurate judgement of how much effort went into the feature}*
-
-* **New Feature**: Added a history command that allows the user to navigate to previous commands using up/down keys.
-
-* **Code contributed**: [RepoSense link]()
-
-* **Project management**:
- * Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub
-
-* **Enhancements to existing features**:
- * Updated the GUI color scheme (Pull requests [\#33](), [\#34]())
- * Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests [\#36](), [\#38]())
-
-* **Documentation**:
- * User Guide:
- * Added documentation for the features `delete` and `find` [\#72]()
- * Did cosmetic tweaks to existing documentation of features `clear`, `exit`: [\#74]()
- * Developer Guide:
- * Added implementation details of the `delete` feature.
-
-* **Community**:
- * PRs reviewed (with non-trivial review comments): [\#12](), [\#32](), [\#19](), [\#42]()
- * Contributed to forum discussions (examples: [1](), [2](), [3](), [4]())
- * Reported bugs and suggestions for other teams in the class (examples: [1](), [2](), [3]())
- * Some parts of the history feature I added was adopted by several other class mates ([1](), [2]())
-
-* **Tools**:
- * Integrated a third party library (Natty) to the project ([\#42]())
- * Integrated a new Github plugin (CircleCI) to the team repo
-
-* _{you can add/remove categories in the list above}_
diff --git a/docs/team/mcnabry.md b/docs/team/mcnabry.md
new file mode 100644
index 00000000000..a3800d7fbf1
--- /dev/null
+++ b/docs/team/mcnabry.md
@@ -0,0 +1,47 @@
+---
+layout: page
+title: Bryan's Project Portfolio Page
+---
+
+
+
+### Project: Jobby
+
+Jobby is a desktop app for managing job applications and contacts. It can help you manage the tracking of your job applications and contacts in a more streamlined fashion.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Added the ability to create contacts for recruiters in the AddressBook.
+ * What it does: Adds a way for the user to track recruiters' contacts.
+ * Justification: Students would need to track the recruiters that recruited them for the internship application.
+
+* **New Feature**: Modified certain fields in organization and recruiter contacts to be optional.
+ * What it does: Users are given the option to leave certain fields blank when adding new organization and recruiter contacts.
+ * Justification: Users might not have all the information and might not want to specify every detail when creating a new contact. Hence, this improves the user experience by making certain fields optional.
+
+* **New Feature**: Added the ability to associate recruiters with organizations.
+ * What it does: Users are able to link recruiters to other organizations added to the AddressBook.
+ * Justification: Recruiters are associated with the organization they work for. Giving users the ability to store this relationship in the AddressBook improves the product's functionality in managing contacts.
+ * Highlights: Given how AB3 was structured, implementing this association was a challenge. There was a struggle between loosely coupling or creating a stronger relationship between the two types of contacts in code. Going with the latter resulted in overhauls to the command's execution and storing of contacts in json. Proper care also had to be taken when details of the contacts changed as the association had to be updated to ensure that the contacts were linked to the latest versions of each other.
+
+* **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=McNaBry&tabRepo=AY2324S1-CS2103T-W08-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~other~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+* **Project management**:
+ * Forked the team repo and set up team organization.
+ * Managed release [v1.2.1](https://github.com/AY2324S1-CS2103T-W08-3/tp/releases/tag/v1.2.1) on GitHub.
+ * Contributed to refactoring `Person` to `Contact` class: [#27](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/27)
+ * Renamed `person` package to `contact`: [#56](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/56)
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for adding recruiter: [#165](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/165)
+ * Added glossary and an appendix detailing acceptable features for the command parameters [#163](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/163)
+ * Developer Guide:
+ * Added documentation on the Organization-Recruiter link: [#173](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/173)
+ * Added appendix on effort taken to evolve AB3 to Jobby: [#190](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/190)
+
+* **Community**:
+ * PRs reviewed (with non-trivial review comments): [#7](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/7), [#164](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/164)
+ * Reported [bugs and suggestions](https://github.com/McNaBry/ped/issues) for other teams.
+
+
diff --git a/docs/team/tanshiyu1999.md b/docs/team/tanshiyu1999.md
new file mode 100644
index 00000000000..b34d8282ef9
--- /dev/null
+++ b/docs/team/tanshiyu1999.md
@@ -0,0 +1,39 @@
+
+
+### Project: Jobby
+
+Jobby is a desktop address book and job application tracking tool. The user interacts with it using a CLI, and it has a GUI created in JavaFX. It is written in Java.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Add Organization
+ * What it does: This adds an organization contact into Jobby
+ * Justification: We want our Jobby application to be able to track both Organization Contact and Recruiter Contact with different parameters, hence we added different functions in order to add these 2 different type of contacts.
+
+* **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=tanshiyu1999&tabRepo=AY2324S1-CS2103T-W08-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~other~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+* **Project management**:
+ * Forked the team repo.
+
+* **Enhancements to existing features**: Modified find command
+ * What it does: Find now is able to find substring instead of the entire keyword as specified by AB3
+ * Justification: The initial find function can only look for the keyword if it is we input the entire keyword. Which is unrealistic as users might not be able to remember the entire keyword. Hence, being able to look up a substring is useful.
+
+* **Enhancements to existing features**: Modified edit command
+ * What it does: Edit target can be selected via both index and the substring for name.
+ * Justification: Initially, edit only works via selection by index. However, we want it to be flexible enough that we can select the target by its name, hence this is being implemented.
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for Add Organization in the User Guide.
+ * Created the starter guide for the documentation.
+ * Help to create GUI Breakdown, and edit the UG for find, edit, apply and delete.
+ * Developer Guide:
+ * Documented the implementation of Add Organization into the Developer Guide.
+ * Added more user stories.
+
+* **Community**:
+ * Reported bugs and suggestions for other teams in the class (examples: During PE-D)
+ * Reviewed PR from Teammates
+
+
diff --git a/docs/team/wamps-jp.md b/docs/team/wamps-jp.md
new file mode 100644
index 00000000000..67d5b1ddb4e
--- /dev/null
+++ b/docs/team/wamps-jp.md
@@ -0,0 +1,43 @@
+---
+layout: page
+title: Juanpa's Project Portfolio Page
+---
+
+
+
+### Project: Jobby
+
+Jobby is a desktop app for tracking job applications. It saves job contacts and applications in an addressbook, which the user can view with a GUI, and interact wtih using a CLI.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Sort command
+ * What it does: Allows users to sort contacts and job applications.
+ * Justification: For reference quality of life, since a job applicant can have many applications and contacts. Makes it easier to view relevant data.
+ * Highlights: Created necessary logic for data to be sorted.
+
+* **New Feature**: Remind command
+ * What it does: Allows users to get a reminder of upcoming or future deadlines.
+ * Justification: Since deadlines can be hard to keep track of, this helps simplify it. Urgent or future deadlines can be viewed easily.
+ * Highlights: Created necessary logic for reminders.
+
+* **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=wamps-jp&tabRepo=AY2324S1-CS2103T-W08-3%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~other~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+* **Project management**:
+ * Helped plan out timeframes for the project.
+
+* **Enhancements to existing features**: List command
+ * Allows users to list organizations, recruiters, or organizations with no applications, instead of just all contacts.
+
+* **Documentation**:
+ * User Guide:
+ * List, sort, and reminder command.
+ * Enhancements to Issues section.
+ * Developer Guide:
+ * Implementation details for sort command.
+ * Added more use cases.
+
+* **Community**:
+ * Reviewed PRs: [#20](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/20), [#27](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/27), [#32](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/32), [#46](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/46), [#96](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/96), [#101](https://github.com/AY2324S1-CS2103T-W08-3/tp/pull/101)
+
+
diff --git a/docs/team/wxwern.md b/docs/team/wxwern.md
new file mode 100644
index 00000000000..24a0d84732a
--- /dev/null
+++ b/docs/team/wxwern.md
@@ -0,0 +1,70 @@
+---
+layout: page
+title: Wern's Project Portfolio Page
+---
+
+
+
+### Project: Jobby
+
+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 ([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**.
+
+ * 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`.
+
+ * **Rationale:** It allows for faster command input, and reduces command syntax memorization.
+
+ * **Implementation:** The internal implementation is written completely from scratch to support our custom formats - more information available in the Developer Guide.
+
+ * **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)
+
+* **Project management**:
+
+ * Configuring the Issue Tracker Tags and Milestones for the project.
+ * 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.
+
+ * 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 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 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).
+ * 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.
+ * Added use cases for autocompletion.
+
+* **Community**:
+ * 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)
+
+
diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md
index d98f38982e7..3f0a73c4c0f 100644
--- a/docs/tutorials/AddRemark.md
+++ b/docs/tutorials/AddRemark.md
@@ -28,7 +28,7 @@ package seedu.address.logic.commands;
import seedu.address.model.Model;
/**
- * Changes the remark of an existing person in the address book.
+ * Changes the remark of an existing contact in the address book.
*/
public class RemarkCommand extends Command {
@@ -65,8 +65,8 @@ Following the convention in other commands, we add relevant messages as constant
``` java
public static final String MESSAGE_USAGE = COMMAND_WORD
- + ": Edits the remark of the person identified "
- + "by the index number used in the last person listing. "
+ + ": Edits the remark of the contact identified "
+ + "by the index number used in the last contact listing. "
+ "Existing remark will be overwritten by the input.\n"
+ "Parameters: INDEX (must be a positive integer) "
+ "r/ [REMARK]\n"
@@ -101,8 +101,8 @@ public class RemarkCommand extends Command {
private final String remark;
/**
- * @param index of the person in the filtered person list to edit the remark
- * @param remark of the person to be updated to
+ * @param index of the contact in the filtered contact list to edit the remark
+ * @param remark of the contact to be updated to
*/
public RemarkCommand(Index index, String remark) {
requireAllNonNull(index, remark);
@@ -151,27 +151,27 @@ Thankfully, `ArgumentTokenizer#tokenize()` makes it trivial to parse user input.
``` java
/**
* Tokenizes an arguments string and returns an {@code ArgumentMultimap}
- * object that maps prefixes to their respective argument values. Only the
- * given prefixes will be recognized in the arguments string.
+ * object that maps flags to their respective argument values. Only the
+ * given flags will be recognized in the arguments string.
*
* @param argsString Arguments string of the form:
- * {@code preamble value value ...}
- * @param prefixes Prefixes to tokenize the arguments string with
- * @return ArgumentMultimap object that maps prefixes to their
+ * {@code preamble value value ...}
+ * @param flags Prefixes to tokenize the arguments string with
+ * @return ArgumentMultimap object that maps flags to their
* arguments
*/
```
-We can tell `ArgumentTokenizer#tokenize()` to look out for our new prefix `r/` and it will return us an instance of `ArgumentMultimap`. Now let’s find out what we need to do in order to obtain the Index and String that we need. Let’s look through `ArgumentMultimap` :
+We can tell `ArgumentTokenizer#tokenize()` to look out for our new flag `r/` and it will return us an instance of `ArgumentMultimap`. Now let’s find out what we need to do in order to obtain the Index and String that we need. Let’s look through `ArgumentMultimap` :
**`ArgumentMultimap.java`:**
``` java
/**
- * Returns the last value of {@code prefix}.
+ * Returns the last value of {@code flag}.
*/
-public Optional getValue(Prefix prefix) {
- List values = getAllValues(prefix);
+public Optional getValue(Prefix flag) {
+ List values = getAllValues(flag);
return values.isEmpty() ? Optional.empty() :
Optional.of(values.get(values.size() - 1));
}
@@ -223,11 +223,11 @@ If you are stuck, check out the sample
## Add `Remark` to the model
-Now that we have all the information that we need, let’s lay the groundwork for propagating the remarks added into the in-memory storage of person data. We achieve that by working with the `Person` model. Each field in a Person is implemented as a separate class (e.g. a `Name` object represents the person’s name). That means we should add a `Remark` class so that we can use a `Remark` object to represent a remark given to a person.
+Now that we have all the information that we need, let’s lay the groundwork for propagating the remarks added into the in-memory storage of contact data. We achieve that by working with the `Person` model. Each field in a Person is implemented as a separate class (e.g. a `Name` object represents the contact’s name). That means we should add a `Remark` class so that we can use a `Remark` object to represent a remark given to a contact.
### Add a new `Remark` class
-Create a new `Remark` in `seedu.address.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code.
+Create a new `Remark` in `seedu.address.model.contact`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code.
A copy-paste and search-replace later, you should have something like [this](https://github.com/se-edu/addressbook-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-41bb13c581e280c686198251ad6cc337cd5e27032772f06ed9bf7f1440995ece). Note how `Remark` has no constrains and thus does not require input
validation.
@@ -238,9 +238,9 @@ Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark`
## Add a placeholder element for remark to the UI
-Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each person.
+Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each contact.
-Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688).
+Simply add the following to [`seedu.address.ui.ContactCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688).
**`PersonCard.java`:**
@@ -293,7 +293,7 @@ While the changes to code may be minimal, the test data will have to be updated
-:exclamation: You must delete AddressBook’s storage file located at `/data/addressbook.json` before running it! Not doing so will cause AddressBook to default to an empty address book!
+:exclamation: You must delete AddressBook’s storage file located at `/data/jobby.json` before running it! Not doing so will cause AddressBook to default to an empty address book!
@@ -309,9 +309,9 @@ Just add [this one line of code!](https://github.com/se-edu/addressbook-level3/c
**`PersonCard.java`:**
``` java
-public PersonCard(Person person, int displayedIndex) {
+public PersonCard(Person contact, int displayedIndex) {
//...
- remark.setText(person.getRemark().value);
+ remark.setText(contact.getRemark().value);
}
```
@@ -335,31 +335,31 @@ save it with `Model#setPerson()`.
//...
@Override
public CommandResult execute(Model model) throws CommandException {
- List lastShownList = model.getFilteredPersonList();
+ List lastShownList = model.getDisplayedContactList();
if (index.getZeroBased() >= lastShownList.size()) {
throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
}
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = new Person(
- personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(),
- personToEdit.getAddress(), remark, personToEdit.getTags());
+ Person contactToEdit = lastShownList.get(index.getZeroBased());
+ Person editedContact = new Person(
+ contactToEdit.getName(), contactToEdit.getPhone(), contactToEdit.getEmail(),
+ contactToEdit.getAddress(), remark, contactToEdit.getTags());
- model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ model.setPerson(contactToEdit, editedContact);
+ model.updateFilteredContactList(PREDICATE_SHOW_ALL_CONTACTS);
- return new CommandResult(generateSuccessMessage(editedPerson));
+ return new CommandResult(generateSuccessMessage(editedContact));
}
/**
* Generates a command execution success message based on whether
* the remark is added to or removed from
- * {@code personToEdit}.
+ * {@code contactToEdit}.
*/
- private String generateSuccessMessage(Person personToEdit) {
+ private String generateSuccessMessage(Person contactToEdit) {
String message = !remark.value.isEmpty() ? MESSAGE_ADD_REMARK_SUCCESS : MESSAGE_DELETE_REMARK_SUCCESS;
- return String.format(message, personToEdit);
+ return String.format(message, contactToEdit);
}
```
diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md
index f29169bc924..4a7c86bb3ef 100644
--- a/docs/tutorials/RemovingFields.md
+++ b/docs/tutorials/RemovingFields.md
@@ -28,7 +28,7 @@ IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a re
### Assisted refactoring
-The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu.
+The `address` field in `Person` is actually an instance of the `seedu.address.model.contact.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu.
* :bulb: To make things simpler, you can unselect the options `Search in comments and strings` and `Search for text occurrences`
![Usages detected](../images/remove/UnsafeDelete.png)
@@ -100,7 +100,7 @@ In `src/test/data/`, data meant for testing purposes are stored. While keeping t
```json
{
- "persons": [ {
+ "contacts": [ {
"name": "Person with invalid name field: Ha!ns Mu@ster",
"phone": "9482424",
"email": "hans@example.com",
diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md
index 4fb62a83ef6..7ae6741d0fc 100644
--- a/docs/tutorials/TracingCode.md
+++ b/docs/tutorials/TracingCode.md
@@ -189,22 +189,22 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
@Override
public CommandResult execute(Model model) throws CommandException {
...
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor);
- if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
+ Person contactToEdit = lastShownList.get(index.getZeroBased());
+ Person editedContact = createEditedPerson(contactToEdit, editPersonDescriptor);
+ if (!contactToEdit.isSamePerson(editedContact) && model.hasContact(editedContact)) {
throw new CommandException(MESSAGE_DUPLICATE_PERSON);
}
- model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson));
+ model.setPerson(contactToEdit, editedContact);
+ model.updateFilteredContactList(PREDICATE_SHOW_ALL_CONTACTS);
+ return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedContact));
}
```
1. As suspected, `command#execute()` does indeed make changes to the `model` object. Specifically,
- * it uses the `setPerson()` method (defined in the interface `Model` and implemented in `ModelManager` as per the usual pattern) to update the person data.
- * it uses the `updateFilteredPersonList` method to ask the `Model` to populate the 'filtered list' with _all_ persons.
- FYI, The 'filtered list' is the list of persons resulting from the most recent operation that will be shown to the user immediately after. For the `edit` command, we populate it with all the persons so that the user can see the edited person along with all other persons. If this was a `find` command, we would be setting that list to contain the search results instead.
- To provide some context, given below is the class diagram of the `Model` component. See if you can figure out where the 'filtered list' of persons is being tracked.
+ * it uses the `setPerson()` method (defined in the interface `Model` and implemented in `ModelManager` as per the usual pattern) to update the contact data.
+ * it uses the `updateFilteredPersonList` method to ask the `Model` to populate the 'filtered list' with _all_ contacts.
+ FYI, The 'filtered list' is the list of contacts resulting from the most recent operation that will be shown to the user immediately after. For the `edit` command, we populate it with all the contacts so that the user can see the edited contact along with all other contacts. If this was a `find` command, we would be setting that list to contain the search results instead.
+ To provide some context, given below is the class diagram of the `Model` component. See if you can figure out where the 'filtered list' of contacts is being tracked.
* :bulb: This may be a good time to read through the [`Model` component section of the DG](../DeveloperGuide.html#model-component)
@@ -231,7 +231,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
* {@code JsonSerializableAddressBook}.
*/
public JsonSerializableAddressBook(ReadOnlyAddressBook source) {
- persons.addAll(
+ contacts.addAll(
source.getPersonList()
.stream()
.map(JsonAdaptedPerson::new)
diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java
index 3d6bd06d5af..77a90798a34 100644
--- a/src/main/java/seedu/address/MainApp.java
+++ b/src/main/java/seedu/address/MainApp.java
@@ -36,7 +36,7 @@
*/
public class MainApp extends Application {
- public static final Version VERSION = new Version(0, 2, 2, true);
+ public static final Version VERSION = new Version(1, 4, 0, true);
private static final Logger logger = LogsCenter.getLogger(MainApp.class);
diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/address/commons/core/LogsCenter.java
index 8cf8e15a0f0..78aad4016c4 100644
--- a/src/main/java/seedu/address/commons/core/LogsCenter.java
+++ b/src/main/java/seedu/address/commons/core/LogsCenter.java
@@ -20,7 +20,7 @@
public class LogsCenter {
private static final int MAX_FILE_COUNT = 5;
private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB
- private static final String LOG_FILE = "addressbook.log";
+ private static final String LOG_FILE = "jobby.log";
private static final Logger logger; // logger for this class
private static Logger baseLogger; // to be used as the parent of all other loggers created by this class.
private static Level currentLogLevel = Level.INFO;
diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/address/commons/core/Version.java
index 491d24559b4..35323cf6808 100644
--- a/src/main/java/seedu/address/commons/core/Version.java
+++ b/src/main/java/seedu/address/commons/core/Version.java
@@ -7,7 +7,7 @@
import com.fasterxml.jackson.annotation.JsonValue;
/**
- * Represents a version with major, minor and patch number
+ * Represents a version with major, minor and patch number.
*/
public class Version implements Comparable {
diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalOperationException.java b/src/main/java/seedu/address/commons/exceptions/IllegalOperationException.java
new file mode 100644
index 00000000000..ea12750e5ad
--- /dev/null
+++ b/src/main/java/seedu/address/commons/exceptions/IllegalOperationException.java
@@ -0,0 +1,21 @@
+package seedu.address.commons.exceptions;
+
+/**
+ * Exception thrown when attempting to make illegal.
+ */
+public class IllegalOperationException extends Exception {
+ /**
+ * @param message that informs the user that it has attempted an illegal operation.
+ */
+ public IllegalOperationException(String message) {
+ super(message);
+ }
+
+ /**
+ * @param message that informs the user that it has attempted an illegal operation.
+ * @param cause of the main exception.
+ */
+ public IllegalOperationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/seedu/address/commons/util/EnumUtil.java b/src/main/java/seedu/address/commons/util/EnumUtil.java
new file mode 100644
index 00000000000..4c0b6166592
--- /dev/null
+++ b/src/main/java/seedu/address/commons/util/EnumUtil.java
@@ -0,0 +1,46 @@
+package seedu.address.commons.util;
+
+import java.util.Arrays;
+
+/**
+ * Helper class to for enums to perform common operations.
+ */
+public class EnumUtil {
+
+ private static final String ENUM_LOOKUP_FAILURE_STRING_FORMAT =
+ "'%s' is not a valid string representation for any enum constants of type '%s'";
+
+ /**
+ * Obtains the enum constant by looking up the enum's {@link Enum#toString()} value that matches the given input.
+ *
+ * @param input The input string to match against the enum's string with.
+ * @param cls The enum class.
+ * @param The enum type.
+ * @return The enum constant.
+ * @throws IllegalArgumentException if the string does not match any of the enum's values.
+ */
+ public static > E lookupByToString(Class cls, String input)
+ throws IllegalArgumentException {
+
+ return Arrays.stream(cls.getEnumConstants())
+ .filter(e -> e.toString().equals(input))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(String.format(
+ ENUM_LOOKUP_FAILURE_STRING_FORMAT,
+ input, cls.getSimpleName()
+ )));
+ }
+
+ /**
+ * Checks whether there exists an enum constant whose {@link Enum#toString()} value matches the given input.
+ *
+ * @param input The input string to match against the enum's string with.
+ * @param cls The enum class to enumerate through.
+ * @param The enum type.
+ * @return true if an enum constant with the given input string as text representation exists, false otherwise.
+ */
+ public static > boolean hasMatchingToString(Class cls, String input) {
+ return Arrays.stream(cls.getEnumConstants()).anyMatch(e -> e.toString().equals(input));
+ }
+
+}
diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/address/commons/util/StringUtil.java
index 61cc8c9a1cb..eaf78a2c5a5 100644
--- a/src/main/java/seedu/address/commons/util/StringUtil.java
+++ b/src/main/java/seedu/address/commons/util/StringUtil.java
@@ -65,4 +65,99 @@ public static boolean isNonZeroUnsignedInteger(String s) {
return false;
}
}
+
+ /**
+ * Formats the given values with the format string, but return null if any of the given values are null or empty.
+ *
+ * @param format The format string to use.
+ * @param values The values to insert into the format string.
+ * @return The formatted string, or null if any of the values are null.
+ */
+ public static String formatWithNullFallback(String format, Object... values) {
+ if (format == null) {
+ return null;
+ }
+
+ for (Object v : values) {
+ if (v == null || v.toString().isBlank()) {
+ return null;
+ }
+ }
+
+ return String.format(format, values);
+ }
+
+ /**
+ * Returns true if the inputString
is a fuzzy match of the targetString
,
+ * false otherwise.
+ *
+ *
+ * A fuzzy search is an approximate search algorithm. This implementation computes a fuzzy match by determining
+ * if there exists a subsequence match in linear time.
+ *
+ *
+ *
+ * As an example, "abc"
is considered to be a fuzzy match of "aa1b2ccc"
, since one may
+ * construct the subsequence "abc"
by removing extra characters "a1"
, "2cc"
+ * from aa1b2ccc
.
+ *
+ *
+ * @param inputString The partial fuzzy input string.
+ * @param targetString The target string to check if the input fuzzily matches with.
+ * @return true if the input fuzzy-matches (is fuzzily contained in) the target, false otherwise.
+ */
+ public static boolean isFuzzyMatch(String inputString, String targetString) {
+ if (inputString == null || targetString == null) {
+ return inputString == null && targetString == null; // both null => true, otherwise false
+ }
+
+ int inputI = 0;
+ int targetI = 0;
+
+ while (inputI < inputString.length() && targetI < targetString.length()) {
+ char c = inputString.charAt(inputI);
+ char t = targetString.charAt(targetI);
+
+ if (c == t) {
+ inputI++;
+ }
+ targetI++;
+ }
+
+ return inputI >= inputString.length();
+ }
+
+ /**
+ * Returns a score representing how close it is to matching characters at the beginning of the target.
+ * The higher the value, the better it is. A failed match will have {@code -targetString.length()}, while
+ * a complete match will have {@code inputString.length()}.
+ *
+ */
+ public static int getFuzzyMatchScore(String inputString, String targetString) {
+ if (inputString == null || targetString == null) {
+ return 0;
+ }
+
+ int inputI = 0;
+ int targetI = 0;
+
+ int errorCount = 0;
+
+ while (inputI < inputString.length() && targetI < targetString.length()) {
+ char c = inputString.charAt(inputI);
+ char t = targetString.charAt(targetI);
+
+ if (c == t) {
+ errorCount += targetI - inputI - errorCount;
+ inputI++;
+ }
+ targetI++;
+ }
+
+ if (inputI < inputString.length()) {
+ return -targetString.length();
+ }
+
+ return inputI - errorCount;
+ }
}
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
index 92cd8fa605a..65f0c9444c3 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/seedu/address/logic/Logic.java
@@ -1,6 +1,7 @@
package seedu.address.logic;
import java.nio.file.Path;
+import java.util.stream.Stream;
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
@@ -8,7 +9,8 @@
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
+import seedu.address.model.contact.Contact;
+import seedu.address.model.jobapplication.JobApplication;
/**
* API of the Logic component
@@ -16,6 +18,7 @@
public interface Logic {
/**
* Executes the command and returns the result.
+ *
* @param commandText The command as entered by the user.
* @return the result of the command execution.
* @throws CommandException If an error occurs during command execution.
@@ -23,6 +26,14 @@ public interface Logic {
*/
CommandResult execute(String commandText) throws CommandException, ParseException;
+ /**
+ * Parses the command and returns any autocompletion results.
+ *
+ * @param commandText The command as entered by the user.
+ * @return the result of the command execution.
+ */
+ Stream generateCompletions(String commandText);
+
/**
* Returns the AddressBook.
*
@@ -30,8 +41,11 @@ public interface Logic {
*/
ReadOnlyAddressBook getAddressBook();
- /** Returns an unmodifiable view of the filtered list of persons */
- ObservableList getFilteredPersonList();
+ /** Returns an unmodifiable view of the filtered and sorted list of contacts. */
+ ObservableList getDisplayedContactList();
+
+ /** Returns an unmodifiable view of the filtered and sorted list of applications. */
+ ObservableList getDisplayedApplicationList();
/**
* Returns the user prefs' address book file path.
@@ -44,7 +58,7 @@ public interface Logic {
GuiSettings getGuiSettings();
/**
- * Set the user prefs' GUI settings.
+ * Sets the user prefs' GUI settings.
*/
void setGuiSettings(GuiSettings guiSettings);
}
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java
index 5aa3b91c7d0..7a7322fd063 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -4,6 +4,7 @@
import java.nio.file.AccessDeniedException;
import java.nio.file.Path;
import java.util.logging.Logger;
+import java.util.stream.Stream;
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
@@ -11,11 +12,12 @@
import seedu.address.logic.commands.Command;
import seedu.address.logic.commands.CommandResult;
import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.logic.parser.AddressBookParser;
+import seedu.address.logic.parser.AppParser;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.Model;
import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
+import seedu.address.model.contact.Contact;
+import seedu.address.model.jobapplication.JobApplication;
import seedu.address.storage.Storage;
/**
@@ -31,7 +33,7 @@ public class LogicManager implements Logic {
private final Model model;
private final Storage storage;
- private final AddressBookParser addressBookParser;
+ private final AppParser appParser;
/**
* Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}.
@@ -39,7 +41,7 @@ public class LogicManager implements Logic {
public LogicManager(Model model, Storage storage) {
this.model = model;
this.storage = storage;
- addressBookParser = new AddressBookParser();
+ appParser = new AppParser();
}
@Override
@@ -47,7 +49,7 @@ public CommandResult execute(String commandText) throws CommandException, ParseE
logger.info("----------------[USER COMMAND][" + commandText + "]");
CommandResult commandResult;
- Command command = addressBookParser.parseCommand(commandText);
+ Command command = appParser.parseCommand(commandText);
commandResult = command.execute(model);
try {
@@ -61,14 +63,26 @@ public CommandResult execute(String commandText) throws CommandException, ParseE
return commandResult;
}
+ @Override
+ public Stream generateCompletions(String commandText) {
+ return appParser
+ .parseCompletionGenerator(commandText)
+ .generateCompletions(commandText, model);
+ }
+
@Override
public ReadOnlyAddressBook getAddressBook() {
return model.getAddressBook();
}
@Override
- public ObservableList getFilteredPersonList() {
- return model.getFilteredPersonList();
+ public ObservableList getDisplayedContactList() {
+ return model.getDisplayedContactList();
+ }
+
+ @Override
+ public ObservableList getDisplayedApplicationList() {
+ return model.getDisplayedApplicationList();
}
@Override
diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java
index ecd32c31b53..4141eade543 100644
--- a/src/main/java/seedu/address/logic/Messages.java
+++ b/src/main/java/seedu/address/logic/Messages.java
@@ -1,11 +1,12 @@
package seedu.address.logic;
+import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import seedu.address.logic.parser.Prefix;
-import seedu.address.model.person.Person;
+import seedu.address.logic.parser.Flag;
+import seedu.address.model.contact.Contact;
/**
* Container for user visible messages.
@@ -14,38 +15,110 @@ public class Messages {
public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
- public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid";
- public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
+ public static final String MESSAGE_INVALID_CONTACT_DISPLAYED_INDEX = "The contact index provided is invalid";
+ public static final String MESSAGE_INVALID_APPLICATION_DISPLAYED_INDEX = "The job application index provided is "
+ + "invalid";
+
+ public static final String MESSAGE_NO_SUCH_CONTACT = "No such contact";
+ public static final String MESSAGE_CONTACTS_LISTED_OVERVIEW = "%1$d contacts listed!";
public static final String MESSAGE_DUPLICATE_FIELDS =
- "Multiple values specified for the following single-valued field(s): ";
+ "Multiple values specified for the following single-valued option(s): ";
+ public static final String MESSAGE_EXTRA_FIELDS =
+ "Extra irrelevant options found in the command: ";
+ public static final String MESSAGE_UNEXPECTED_NON_EMPTY_FIELDS =
+ "The following options may not have any value: ";
+ public static final String MESSAGE_SIMULTANEOUS_USE_DISALLOWED_FIELDS =
+ "The following options conflict and cannot be set together: ";
+
+ public static final String MESSAGE_INVALID_FIELD =
+ "The term '%s' is not a valid option!";
/**
- * Returns an error message indicating the duplicate prefixes.
+ * Returns an error message indicating the duplicate flags.
*/
- public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePrefixes) {
- assert duplicatePrefixes.length > 0;
+ public static String getErrorMessageForDuplicateFlags(Flag... duplicateFlags) {
+ assert duplicateFlags.length > 0;
Set duplicateFields =
- Stream.of(duplicatePrefixes).map(Prefix::toString).collect(Collectors.toSet());
+ Stream.of(duplicateFlags).map(Flag::toString).collect(Collectors.toSet());
+
+ return MESSAGE_DUPLICATE_FIELDS + String.join(", ", duplicateFields);
+ }
+
+ /**
+ * Returns an error message indicating the extraneous flags.
+ */
+ public static String getErrorMessageForExtraneousFlags(Flag... extraneousFlags) {
+ assert extraneousFlags.length > 0;
+
+ Set extraneousFields =
+ Stream.of(extraneousFlags).map(Flag::toString).collect(Collectors.toSet());
+
+ return MESSAGE_EXTRA_FIELDS + String.join(", ", extraneousFields);
+ }
+
+ /**
+ * Returns an error message indicating the flags have unexpected values.
+ */
+ public static String getErrorMessageForNonEmptyValuedFlags(Flag... nonEmptyValuedFlags) {
+ assert nonEmptyValuedFlags.length > 0;
+
+ Set nonEmptyValuedFields =
+ Stream.of(nonEmptyValuedFlags).map(Flag::toString).collect(Collectors.toSet());
- return MESSAGE_DUPLICATE_FIELDS + String.join(" ", duplicateFields);
+ return MESSAGE_UNEXPECTED_NON_EMPTY_FIELDS + String.join(", ", nonEmptyValuedFields);
}
/**
- * Formats the {@code person} for display to the user.
+ * Returns an error message indicating the flags should not exist in the same command together.
*/
- public static String format(Person person) {
+ public static String getErrorMessageForSimultaneousUseDisallowedFlags(Flag... conflictingFlags) {
+ assert conflictingFlags.length > 1;
+
+ Set conflictingFields =
+ Stream.of(conflictingFlags).map(Flag::toString).collect(Collectors.toSet());
+
+ return MESSAGE_SIMULTANEOUS_USE_DISALLOWED_FIELDS + String.join(", ", conflictingFields);
+ }
+
+ /**
+ * Returns an error message indicating the invalid flag.
+ */
+ public static String getErrorMessageForInvalidFlagString(String flagString) {
+ return String.format(
+ MESSAGE_INVALID_FIELD, flagString
+ );
+ }
+
+ /**
+ * Formats the {@code contact} for display to the user.
+ */
+ public static String format(Contact contact) {
final StringBuilder builder = new StringBuilder();
- builder.append(person.getName())
+ builder.append(contact.getName())
+ .append("; Id: ")
+ .append(contact.getId())
.append("; Phone: ")
- .append(person.getPhone())
+ .append(contact.getPhone().map(p -> p.value).orElse("(none)"))
.append("; Email: ")
- .append(person.getEmail())
+ .append(contact.getEmail().map(e -> e.value).orElse("(none)"))
+ .append("; Url: ")
+ .append(contact.getUrl().map(u -> u.value).orElse("(none)"))
.append("; Address: ")
- .append(person.getAddress())
+ .append(contact.getAddress().map(a -> a.value).orElse("(none)"))
.append("; Tags: ");
- person.getTags().forEach(builder::append);
+ contact.getTags().forEach(builder::append);
return builder.toString();
}
+ /**
+ * Formats the given {@code childrenContacts} for display to the user.
+ */
+ public static String formatChildren(List childrenContacts) {
+ return childrenContacts.stream()
+ .map(c -> Messages.format(c) + "\n")
+ .reduce((c1, c2) -> c1 + c2)
+ .orElse("No other contacts found");
+ }
+
}
diff --git a/src/main/java/seedu/address/logic/autocomplete/AutocompleteGenerator.java b/src/main/java/seedu/address/logic/autocomplete/AutocompleteGenerator.java
new file mode 100644
index 00000000000..5db6a0a6e06
--- /dev/null
+++ b/src/main/java/seedu/address/logic/autocomplete/AutocompleteGenerator.java
@@ -0,0 +1,163 @@
+package seedu.address.logic.autocomplete;
+
+import java.util.Comparator;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import seedu.address.commons.util.StringUtil;
+import seedu.address.logic.autocomplete.components.PartitionedCommand;
+import seedu.address.logic.parser.Flag;
+import seedu.address.model.Model;
+
+/**
+ * Creates a generator based on the given supplier or reference commands, so as it can generate
+ * auto-completions when requested.
+ */
+public class AutocompleteGenerator {
+
+ /** An autocompletion generator that generates no results. */
+ public static final AutocompleteGenerator NO_RESULTS = new AutocompleteGenerator(Stream::empty);
+
+
+ /** A comparator used to order fuzzily matched strings where better matches against the input go first. */
+ private static final Function> TEXT_FUZZY_MATCH_COMPARATOR = (input) -> (s1, s2) -> {
+ // Get how well s1 is ahead of s2 (note: higher is better).
+ int score = StringUtil.getFuzzyMatchScore(input, s1) - StringUtil.getFuzzyMatchScore(input, s2);
+
+ return -score; // -ve implies s1 < s2
+ };
+
+ /** A comparator used to order fuzzily matched flags where better matches against the input go first. */
+ private static final Function> FLAG_FUZZY_MATCH_COMPARATOR = (input) -> (f1, f2) -> {
+ // Get how well f1 is ahead of f2 in both metrics (note: higher is better).
+ int score = Math.max(
+ StringUtil.getFuzzyMatchScore(input, f1.getFlagString()),
+ StringUtil.getFuzzyMatchScore(input, f1.getFlagAliasString())
+ ) - Math.max(
+ StringUtil.getFuzzyMatchScore(input, f2.getFlagString()),
+ StringUtil.getFuzzyMatchScore(input, f2.getFlagAliasString())
+ );
+
+ return -score; // -ve implies f1 < f2
+ };
+
+
+
+ /** The cached instance of the result evaluation function. */
+ private final BiFunction> resultEvaluationFunction;
+
+ /**
+ * Constructs an autocomplete generator based on the given supplier of full command strings.
+ */
+ public AutocompleteGenerator(Supplier> referenceCommandsSupplier) {
+ resultEvaluationFunction = (partialCommand, model) -> {
+ String input = partialCommand == null ? "" : partialCommand;
+
+ return referenceCommandsSupplier.get()
+ .filter(term -> StringUtil.isFuzzyMatch(input, term))
+ .sorted(TEXT_FUZZY_MATCH_COMPARATOR.apply(partialCommand))
+ .distinct();
+ };
+ }
+
+ /**
+ * Constructs an autocomplete generator based on the given {@link AutocompleteSupplier}.
+ */
+ public AutocompleteGenerator(AutocompleteSupplier supplier) {
+ resultEvaluationFunction = (partialCommand, model) -> {
+
+ PartitionedCommand command = new PartitionedCommand(partialCommand == null ? "" : partialCommand);
+ String trailingText = command.getTrailingText();
+
+ // Compute the possible flags and flag-values.
+ Stream possibleFlags = getPossibleFlags(command, supplier)
+ .filter(flag -> StringUtil.isFuzzyMatch(trailingText, flag.getFlagString())
+ || StringUtil.isFuzzyMatch(trailingText, flag.getFlagAliasString()))
+ .sorted(FLAG_FUZZY_MATCH_COMPARATOR.apply(trailingText))
+ .map(Flag::getFlagString);
+
+ Stream possibleValues = getPossibleValues(command, supplier, model)
+ .map(s -> s.filter(term -> StringUtil.isFuzzyMatch(trailingText, term))
+ .sorted(TEXT_FUZZY_MATCH_COMPARATOR.apply(trailingText)))
+ .orElse(possibleFlags);
+
+ // Decide which stream to use based on whether it's of a flag syntax or not.
+ Stream possibleTerminalValues;
+ if (command.hasFlagSyntaxPrefixInTrailingText()) {
+ possibleTerminalValues = possibleFlags;
+ } else {
+ possibleTerminalValues = possibleValues;
+ }
+
+ // Return the results as a full completion string, distinct.
+ return possibleTerminalValues
+ .map(command::toStringWithNewTrailingTerm)
+ .distinct();
+ };
+ }
+
+ /**
+ * Generates a stream of completions based on the partial command given and no model.
+ * Note that omitting the model may limit the ability to return useful results.
+ */
+ public Stream generateCompletions(String command) {
+ return resultEvaluationFunction.apply(command, null);
+ }
+
+ /**
+ * Generates a stream of completions based on the partial command given and the model.
+ */
+ public Stream generateCompletions(String command, Model model) {
+ return resultEvaluationFunction.apply(command, model);
+ }
+
+
+
+
+ /**
+ * Obtains the set of possible flags based on the partitioned command and supplier.
+ */
+ private static Stream getPossibleFlags(
+ PartitionedCommand command,
+ AutocompleteSupplier supplier
+ ) {
+ Flag[] allPossibleFlags = supplier.getAllPossibleFlags().toArray(Flag[]::new);
+
+ Set existingCommandFlags = command.getConfirmedFlagStrings()
+ .stream()
+ .map(flagStr -> Flag.findMatch(flagStr, allPossibleFlags))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .collect(Collectors.toSet());
+
+
+ return supplier.getOtherPossibleFlagsAsideFromFlagsPresent(existingCommandFlags).stream();
+ }
+
+ /**
+ * Obtains the optional set of possible values based on the partitioned command, supplier, and model.
+ * If this optional is empty, that means it is explicitly specified that the flag cannot accept values.
+ */
+ private static Optional> getPossibleValues(
+ PartitionedCommand command,
+ AutocompleteSupplier supplier,
+ Model model
+ ) {
+ Optional flagString = command.getLastConfirmedFlagString();
+ if (flagString.isEmpty()) {
+ return supplier.getValidValuesForFlag(null, command, model);
+ }
+
+ Optional targetFlag = Flag.findMatch(
+ flagString.get(),
+ supplier.getAllPossibleFlags().toArray(Flag[]::new)
+ );
+ 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
new file mode 100644
index 00000000000..1c259b387d6
--- /dev/null
+++ b/src/main/java/seedu/address/logic/autocomplete/AutocompleteSupplier.java
@@ -0,0 +1,147 @@
+package seedu.address.logic.autocomplete;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+import seedu.address.logic.autocomplete.components.AutocompleteItemSet;
+import seedu.address.logic.autocomplete.components.FlagValueSupplier;
+import seedu.address.logic.autocomplete.components.PartitionedCommand;
+import seedu.address.logic.parser.Flag;
+import seedu.address.model.Model;
+
+/**
+ * Supplies autocompletion details for arbitrary commands.
+ */
+public class AutocompleteSupplier {
+
+ private final AutocompleteItemSet flags;
+ private final Map values;
+
+ /**
+ * Constructs an autocomplete supplier that is capable of supplying useful details for autocompleting some command.
+ * The ordering supplied via the parameters are followed when generating results.
+ *
+ * @param flags The set of flags that should be used as part of the autocomplete results.
+ * @param values A map of auto-completable values for each flag that may be obtained via a model.
+ */
+ public AutocompleteSupplier(
+ AutocompleteItemSet flags,
+ Map values
+ ) {
+ // Create new copies to prevent external modification.
+ this.flags = flags.copy();
+ this.values = new LinkedHashMap<>(values);
+ }
+
+ /**
+ * Constructs an autocomplete supplier that is capable of supplying useful details for autocompleting some command.
+ * The ordering supplied via the parameters are followed when generating results.
+ *
+ * @param flags The set of flags that should be used as part of the autocomplete results.
+ *
+ * @see #AutocompleteSupplier(AutocompleteItemSet, Map)
+ */
+ public static AutocompleteSupplier from(AutocompleteItemSet flags) {
+ return new AutocompleteSupplier(flags, Map.of());
+ }
+
+ /**
+ * Constructs an autocomplete supplier that is capable of supplying useful details for autocompleting some command.
+ * The ordering supplied via the parameters are followed when generating results.
+ *
+ * @param flagSets The sets of flags that should be used together as part of the autocomplete results.
+ */
+ @SafeVarargs
+ public static AutocompleteSupplier from(AutocompleteItemSet... flagSets) {
+ return from(AutocompleteItemSet.concat(flagSets));
+ }
+
+ /**
+ * Constructs an autocomplete supplier that is capable of supplying all the unique flags (flags which may appear
+ * at most once) for autocompleting some command.
+ * The ordering supplied via the parameters are followed when generating results.
+ *
+ * @param uniqueFlags A set of flags that each may appear at most once in the command.
+ */
+ public static AutocompleteSupplier fromUniqueFlags(Flag... uniqueFlags) {
+ return AutocompleteSupplier.from(
+ AutocompleteItemSet.onceForEachOf(uniqueFlags)
+ );
+ }
+
+ /**
+ * Constructs an autocomplete supplier that is capable of supplying all the repeatable flags (flags which may
+ * appear any number of times in the command) for autocompleting some command.
+ * The ordering supplied via the parameters are followed when generating results.
+ *
+ * @param repeatableFlags A set of flags that may appear at any point in the command any number of times.
+ */
+ public static AutocompleteSupplier fromRepeatableFlags(Flag... repeatableFlags) {
+ return AutocompleteSupplier.from(
+ AutocompleteItemSet.anyNumberOf(repeatableFlags)
+ );
+ }
+
+ /**
+ * Returns a set of all possible flags that can be used in the command.
+ * The set has predictable iteration order: it follows the ordering supplied via the original inputs.
+ */
+ public Set getAllPossibleFlags() {
+ return flags.copy();
+ }
+
+ /**
+ * Returns a set of other possible flags given the list of flags that are already present in the command.
+ * If there are conflicting constraints specified, this will use the tightest possible constraint.
+ * The set has predictable iteration order: it follows the ordering supplied via the original inputs.
+ */
+ public Set getOtherPossibleFlagsAsideFromFlagsPresent(Set flagsPresent) {
+ return flags.getElementsAfterConsuming(flagsPresent);
+ }
+
+ /**
+ * Returns an optional stream of possible values for a flag when computed against a given model.
+ * If this optional is empty, then this flag is explicitly specified to not have any values,
+ * and not just the lack of completion suggestions.
+ *
+ * @param flag The flag to check against. This may be null to represent the preamble.
+ * @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> getValidValuesForFlag(Flag flag, PartitionedCommand currentCommand, Model model) {
+ try {
+ return Optional.ofNullable(
+ this.values.getOrDefault(flag, (c, m) -> Stream.empty())
+ ).map(flagValueSupplier -> flagValueSupplier.apply(currentCommand, model));
+
+ } catch (RuntimeException e) {
+ // Guard against errors like NPEs due to supplied lambdas not handling them.
+ e.printStackTrace();
+ // We simply return that we don't know what to auto-complete by.
+ return Optional.of(Stream.of());
+ }
+ }
+
+ /**
+ * Configures the set of flags within this autocomplete supplier using the given {@code operator}.
+ * This also returns {@code this} instance, which is useful for chaining.
+ */
+ public AutocompleteSupplier configureFlagSet(Consumer> operator) {
+ operator.accept(this.flags);
+ return this;
+ }
+
+ /**
+ * Configures the map of values within this autocomplete supplier using the given {@code operator}.
+ * This also returns {@code this} instance, which is useful for chaining.
+ */
+ public AutocompleteSupplier configureValueMap(Consumer