├── .gitignore
├── .npmignore
├── .vscode
└── launch.json
├── README.md
├── category-aliases-db.json
├── controllers
├── db-routes.js
└── json-routes.js
├── dist
├── 3rdpartylicenses.txt
├── assets
│ └── sakura-dark.css
├── favicon.ico
├── index.html
├── main.js
├── main.js.map
├── polyfills.js
├── polyfills.js.map
├── runtime.js
├── runtime.js.map
├── styles.js
├── styles.js.map
├── vendor.js
└── vendor.js.map
├── index.js
├── package-lock.json
├── package.json
├── readme-images
├── snippets-export.png
├── snippets-intro.png
├── snippets-outro.png
├── snippets-part-1.gif
├── snippets-part-2.gif
├── snippets-part-3.gif
├── snippets-results.png
└── snippets-snippet.png
├── routes.js
├── snippets.code-workspace
└── snippets
├── .editorconfig
├── .gitignore
├── README.md
├── angular.json
├── e2e
├── app.e2e-spec.ts
├── app.po.ts
└── tsconfig.e2e.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── protractor.conf.js
├── src
├── app
│ ├── add-snippet
│ │ ├── add-snippet.component.css
│ │ ├── add-snippet.component.html
│ │ ├── add-snippet.component.spec.ts
│ │ └── add-snippet.component.ts
│ ├── app.component.css
│ ├── app.component.html
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── build-cover-letter
│ │ ├── build-cover-letter.component.css
│ │ ├── build-cover-letter.component.html
│ │ ├── build-cover-letter.component.spec.ts
│ │ └── build-cover-letter.component.ts
│ ├── category-aliases-db
│ │ ├── category-aliases-db.component.css
│ │ ├── category-aliases-db.component.html
│ │ ├── category-aliases-db.component.spec.ts
│ │ └── category-aliases-db.component.ts
│ ├── cover-letter
│ │ ├── cover-letter.component.css
│ │ ├── cover-letter.component.html
│ │ ├── cover-letter.component.spec.ts
│ │ └── cover-letter.component.ts
│ ├── database
│ │ ├── database.component.css
│ │ ├── database.component.html
│ │ ├── database.component.spec.ts
│ │ └── database.component.ts
│ ├── export
│ │ ├── export.component.css
│ │ ├── export.component.html
│ │ ├── export.component.spec.ts
│ │ └── export.component.ts
│ ├── has-keywords.pipe.spec.ts
│ ├── has-keywords.pipe.ts
│ ├── job-description
│ │ ├── job-description.component.css
│ │ ├── job-description.component.html
│ │ ├── job-description.component.spec.ts
│ │ └── job-description.component.ts
│ ├── nav
│ │ ├── nav.component.css
│ │ ├── nav.component.html
│ │ ├── nav.component.spec.ts
│ │ └── nav.component.ts
│ ├── results
│ │ ├── results.component.css
│ │ ├── results.component.html
│ │ ├── results.component.spec.ts
│ │ └── results.component.ts
│ ├── services
│ │ ├── cover-letter.service.spec.ts
│ │ ├── cover-letter.service.ts
│ │ ├── database.service.db.spec.ts
│ │ ├── database.service.json.spec.ts
│ │ ├── database.service.ts
│ │ ├── databaseCategoryAliases.service.ts
│ │ ├── parse-description.service.spec.ts
│ │ ├── parse-description.service.ts
│ │ ├── status-message.service.spec.ts
│ │ └── status-message.service.ts
│ └── status-message
│ │ ├── status-message.component.css
│ │ ├── status-message.component.html
│ │ ├── status-message.component.spec.ts
│ │ └── status-message.component.ts
├── assets
│ ├── .gitkeep
│ └── sakura-dark.css
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── styles.css
├── test-helpers.js
├── test.ts
├── tsconfig.app.json
├── tsconfig.spec.json
└── typings.d.ts
├── tsconfig.json
└── tslint.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 |
63 | # vscode workspace
64 | *.code-workspace
65 |
66 | *snippets-db.json
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Ignore the Angular Development App
2 | /snippets/
3 |
4 | # Ignore Created DB.
5 | ./snippets-db.json
6 |
7 | # vscode workspace
8 | *.code-workspace
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Unit tests",
9 | "type": "chrome",
10 | "request": "attach",
11 | "address": "localhost",
12 | "port": 9333,
13 | "webRoot": "${workspaceFolder}/snippets",
14 | // "outFiles": [
15 | // "${workspaceFolder}/../dist/*.js"
16 | // ]
17 | },
18 | {
19 | "name": "ng test",
20 | "type": "chrome",
21 | "request": "launch",
22 | "url": "http://localhost:9876/debug.html",
23 | "webRoot": "${workspaceFolder}"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cover Letter Snippets
2 | Cover Letter Snippets is an Angular/MongoDB/Node.js Express application that helps you figure out the keywords on a job listing and then compose a cover letter customized for the job by reusing the relevant content from your previous cover letters.
3 |
4 |
6 |
7 | ## Installing and Running Cover Letter Snippets
8 |
9 | Note: The following instructions have been tested for MacOS only.
10 |
11 | To install and run Cover Letter Snippets:
12 | 1. Run `npm i cover-letter-snippets`
13 |
14 | Note: In some cases, you might have to install the package globally: `npm i -g cover-letter-snippets`
15 |
16 | 2. Run `snippets`
17 |
18 | The application opens on the browser at `localhost:3141`.
19 |
20 | ## Configuring Cover Letter Snippets
21 |
22 | The easiest and recommended way to configure the application is to save the cover letter snippets as a JSON file on your local machine. To do so, click **Connect to JSON file** on the Database page.
23 |
24 | However, if you want to use the application across computers, you can store the cover letter snippets in a MongoDB database on MLab.com. This is an advanced use-case. For instructions to save the snippets in a MongoDB database, see [Save snippets in a MongoDB database](https://github.com/mattcheah/cover-letter-snippets#save-the-snippets-in-a-mongodb-database-optional).
25 |
26 | ## Using Cover Letter Snippets
27 |
28 | 
29 |
30 | ### Adding snippets
31 |
32 | Note: To add a snippet to the database, you need to have your existing cover letter(s) handy.
33 |
34 | 1. Navigate to the **Add Snippet** page.
35 | 2. From your existing cover letter, copy and paste a snippet related to one or more skills or technology. For example, add one snippet for a paragraph that describes your experience with Python. Add another snippet describing a side-project involving Go.
36 | 3. For each snippet, add the relevant keywords (comma separated, no spaces) in the **Categories** field.
37 |
38 | Important: The category names are case-sensitive.
39 |
40 | 4. Click **Submit Snippet**.
41 | 5. Add `intro` snippet and add the keyword `intro` to its **Categories** field. Click **Submit Snippet**.
42 | 6. Add an `outro` snippet and add the keyword `outro` in its **Categories** field. Click **Submit Snippet**.
43 |
44 | ### Editing snippets and categories
45 |
46 | To edit the snippets and their categories:
47 |
48 | 1. Navigate to the **Database** page. A table displaying the cover letter snippets is displayed.
49 | 2. Click on the snippet you want to edit and make the desired changes.
50 | 3. Click on the category you want to edit and make the desired changes.
51 | 4. Click off the table to save the changes.
52 |
53 | ### Building the cover letter
54 |
55 | To build the cover letter:
56 |
57 | 1. Navigate to the **Build Cover Letter** page.
58 | 2. Copy the job description for the job you want to apply to. Paste the job description in the text box.
59 | 3. Click **Parse Job Description**. A table containing a list of keywords, their frequency in the job description, and the relevant snippets appears.
60 | 
61 | 4. Click **Show More**.
62 | 5. For the relevant snippet, click **Show Snippet**.
63 | 6. If you decide to include the snippet in the cover letter, click **Add** in the **Add to Letter** column.
64 | 7. Repeat step 6 for all relevant snippets.
65 | 8. Repeat steps 4-6 to add intro and outro snippets.
66 | 
67 | 9. Reorder the snippets by clicking **Move Up** and **Move Down** as required.
68 | 10. Once you are done making all changes, click **Export and Tweak Cover Letter**. The Exported Cover Letter appears on the **Export Cover Letter** page.
69 | 11. Copy and paste the contents of the cover letter into a Word doc or Google doc.
70 |
71 | 
72 |
73 | ### Saving the snippets in a MongoDB database (optional)
74 |
75 | Note: This is an advanced use-case.
76 |
77 | If you plan to use the application across computers, you can store the cover letter snippets in the preconfigured MongoDB database or your own MongoDB database on MLab.com.
78 |
79 | If you want to play around with the app, there is a preconfigured database URI string in the form field. Please be a good person or I will have to take it out. Also, after you are done trying out the app, delete your entries from the database to ensure that other users don't see your entries.
80 |
81 | Alternatively, to store the snippets in your MongoDB database:
82 | 1. Go to mlab.com and sign up or log in.
83 | 2. Click **Create New** under Deployments.
84 | 3. Choose **AWS** and the **Free Sandbox** plan.
85 | 4. Pick a region and name your database.
86 | 5. Follow the prompts to submit and create your database.
87 | 6. Click on your new database.
88 | 7. Navigate to the **Users** tab and create a new user.
89 | 8. Fill out the username and password.
90 | 9. Copy the MongoDB URI for your database.
91 | 10. Navigate to the Cover Letter Snippets application. On the **Database** page, paste the MongoDB URI for your database. Replace the username/password with your credentials in the URI.
92 | 11. Click **Connect to Database**.
93 |
94 | # Contributing
95 |
96 | If you'd like to help, there's a ton of work that can be done to improve the product. Clone the repo and make a pull request! Off the top of my head, here are some things that can be done:
97 |
98 | - [Show Intro Snippets automatically after parsing job description](https://github.com/mattcheah/cover-letter-snippets/issues/1)
99 | - [Add button to show more snippets even if they haven't been mentioned in job desc.](https://github.com/mattcheah/cover-letter-snippets/issues/2)
100 | - [Create a fixed div as a sidebar that shows the parsed job description when you're adding snippets.](https://github.com/mattcheah/cover-letter-snippets/issues/4)
101 | - [Create an array of aliases for category names](https://github.com/mattcheah/cover-letter-snippets/issues/5) (ie. a job description listing 'RoR' or 'Ruby/Rails' would match your category of 'rails'. Each category and aliases would have to be set up by the user individually.)
102 | - [Sort addable snippets by which ones have the most relevant categories in the job description.](https://github.com/mattcheah/cover-letter-snippets/issues/6)
103 |
104 | # Other
105 |
106 | If you're looking to build a quick and easy resume, check out [Best Resume Ever](https://github.com/salomonelli/best-resume-ever) - I created the purple theme, which was based on how I styled my own resume. Check it out!
107 |
--------------------------------------------------------------------------------
/category-aliases-db.json:
--------------------------------------------------------------------------------
1 | [{"_id":322143,"category":"cat10\n","aliases":["a1","a2"]},{"_id":247812,"category":"php\n\n","aliases":["php11"]}]
--------------------------------------------------------------------------------
/controllers/db-routes.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | // CRUD routes for Mongoose db snippets entries.
4 | module.exports = {
5 | connectToDatabase: (req, res) => {
6 |
7 | const dburl = req.body.databaseUrl;
8 |
9 | if (dburl == "") {
10 | res.status(400).send("Please enter the URI of a mlab mongodb database!");
11 | return;
12 | }
13 |
14 | let connection = mongoose.connect(dburl);
15 | mongoose.connection.on('error', function (err) {
16 | console.log("Error! " + err);
17 | res.status(400).json({ error: "Sorry, mongoose could not connect to the database! Make sure your URI is correct!" });
18 | return;
19 | });
20 | mongoose.connection.on('connected', function () {
21 | console.log("Successfully Connected!");
22 | });
23 |
24 |
25 | let Snippet = module.exports.setUpSchema();
26 | module.exports.getAllSnippets(Snippet, "Successfully Connected to the Database and retrieved records!").then(data => {
27 | res.json(data);
28 | });
29 | },
30 | addSnippet: (req, res) => {
31 | const snippet = req.body.snippet;
32 | const categories = req.body.categories;
33 | let Snippet = module.exports.setUpSchema();
34 | new Snippet({
35 | snippet: snippet,
36 | categories: categories
37 | }).save(function (err) {
38 | if (err) {
39 | console.log("Error saving new snippet: " + err);
40 | res.error("Error adding snippet: " + err);
41 | } else {
42 | console.log("New Snippet Saved!");
43 | module.exports.getAllSnippets(Snippet, "New Snippet Saved!").then(data => {
44 | res.json(data);
45 | });
46 | }
47 | });
48 | },
49 | editSnippet: (req, res) => {
50 | let Snippet = module.exports.setUpSchema();
51 |
52 | const id = req.body.id;
53 | const snippet = req.body.snippet;
54 | const categories = req.body.categories;
55 |
56 | Snippet.findById(id, function (err, response) {
57 | if(err) {
58 | res.status(400).json({
59 | error: true,
60 | connected: true,
61 | data: [],
62 | responseMessage: err
63 | });
64 | }
65 | response.snippet = snippet;
66 | response.categories = categories;
67 | response.save((err) => {
68 | if (err) {
69 | res.status(500).json({
70 | error: true,
71 | connected: true,
72 | data: [],
73 | responseMessage: err
74 | });
75 | }
76 | module.exports.getAllSnippets(Snippet, "Edited Snippet: " + snippet.substring(0, 30) + "...").then(data => {
77 | res.json(data);
78 | });
79 | });
80 | });
81 |
82 | },
83 | deleteSnippet: (req, res) => {
84 | const Snippet = module.exports.setUpSchema();
85 | const id = req.body.id;
86 | let snippet;
87 | Snippet.findById(id, function (err, response) {
88 | let snippet = response;
89 | Snippet.findByIdAndRemove(id, function (err) {
90 | if (err) res.error("Error deleting snippet: " + err);
91 | module.exports.getAllSnippets(Snippet, "Removed Snippet: " + snippet.snippet.substring(0, 30) + "...").then(data => {
92 | res.json(data);
93 | });
94 | });
95 | });
96 | },
97 | getAllSnippets: (Snippet, successMessage) => {
98 | // Get all the snippets in the database;
99 | return new Promise((res, rej) => {
100 | Snippet.find({}, function (err, data) {
101 | if (err) {
102 | rej({
103 | error: true,
104 | connected: false,
105 | data: [],
106 | responseMessage: err
107 | });
108 | }
109 | returnObj = {
110 | connected: true,
111 | error: false,
112 | responseMessage: successMessage,
113 | data: data
114 | };
115 | res(returnObj);
116 | });
117 | });
118 | },
119 | setUpSchema: () => {
120 | try {
121 | Snippet = mongoose.model('Snippet');
122 | } catch (error) {
123 | let Schema = mongoose.Schema;
124 | let snippetSchema = new Schema({
125 | categories: Array,
126 | snippet: String
127 | });
128 | Snippet = mongoose.model('Snippet', snippetSchema);
129 | }
130 | return Snippet;
131 | }
132 | }
--------------------------------------------------------------------------------
/controllers/json-routes.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | // CRUD routes for JSON file snippets entries.
5 | const self = module.exports = {
6 | jsonFilePath: "",
7 | aliasJsonFilePath: "",
8 | getSnippets: (req,res) => {
9 | console.log("Called GetSnippets!");
10 | self.jsonFilePath = path.normalize(req.body.databaseUrl);
11 |
12 | //create file if it doesn't exist.
13 | try {
14 |
15 | fs.open(self.jsonFilePath, "wx", function (err, fd) {
16 | if (err) {
17 | // throw(err);
18 | console.log("File exists already");
19 | } else {
20 | let newJson = [
21 | {
22 | snippet: "This is an example snippet from a new JSON file. Delete me, create more snippets with cover letter text, and categorize them appropriately!",
23 | categories: ["intro","cover","letter","snippets"]
24 | }
25 | ];
26 | self.writeJson(newJson, ()=> {
27 | fs.close(fd, function (err) {
28 | if (err) {
29 | console.log("error closing created json file: " + err);
30 | }
31 | });
32 | });
33 | }
34 |
35 | });
36 | } catch(err) {
37 | console.log("Error: Thrown error");
38 | console.log(err);
39 | }
40 |
41 | self.returnJsonData().then(data => {
42 | let returnObj = {
43 | responseMessage: "Connected to JSON file and Returned Results!",
44 | connected: true,
45 | data: data
46 | };
47 | res.json(returnObj);
48 | }).catch(err => {
49 | res.status(500).json({
50 | error: true,
51 | responseMessage: "Sorry, could not connect to JSON file",
52 | connected: false,
53 | data: []
54 | });
55 | });
56 | },
57 | addSnippet: (req,res) => {
58 | console.log("calling addSnippet");
59 | const snippet = req.body.snippet;
60 | const categories = req.body.categories;
61 |
62 |
63 | self.returnJsonData().then(data => {
64 | console.log("promise return from addSnippet");
65 | let db = data;
66 | const snippetObject = {
67 | _id: self.createUniqueId(db),
68 | snippet: snippet,
69 | categories: categories
70 | };
71 |
72 | db.push(snippetObject);
73 | console.log("db in json routes add snippet: ");
74 | console.log(db);
75 | self.writeJson(db, () => {
76 | console.log("Added snippet:");
77 | console.log(snippet);
78 | console.log(categories);
79 | res.json(self.createResponseObject(db, "Added Snippet to the Database Successfully!"));
80 | });
81 |
82 | }).catch(err => {
83 | let returnObj = {
84 | error: true,
85 | responseMessage: err,
86 | data: [],
87 | connected: true
88 | }
89 | res.status(500).json(returnObj);
90 | });
91 | },
92 | editSnippet: (req,res) => {
93 | const id = req.body.id;
94 | const snippet = req.body.snippet;
95 | const categories = req.body.categories;
96 | const snippetObject = {
97 | snippet: snippet,
98 | categories: categories
99 | };
100 |
101 | self.returnJsonData().then(data => {
102 | let db = data;
103 | let foundSnippet = false;
104 |
105 | for(let i = 0; i < db.length; i++) {
106 | if (db[i]._id === id) {
107 | db[i].snippet = snippet;
108 | db[i].categories = categories;
109 | foundSnippet = true;
110 | }
111 | }
112 |
113 | if (foundSnippet) {
114 | self.writeJson(db, ()=> {
115 | res.json(self.createResponseObject(db, "Edited your snipped with an id of: " + id));
116 | });
117 | } else {
118 | self.catchError("Could not find snippet with an id of: " + id, res, 400);
119 | }
120 |
121 | }).catch((err) => {
122 | self.catchError(err, res);
123 | });
124 | },
125 | deleteSnippet: (req,res) => {
126 | const id = req.body.id;
127 | self.returnJsonData().then(data => {
128 | let db = data;
129 | let foundSnippet = false;
130 |
131 | for (let i = 0; i < db.length; i++) {
132 | if (db[i]._id === id) {
133 | //Splice this element out of the db.
134 | db.splice(i, 1);
135 | foundSnippet = true;
136 | }
137 | }
138 |
139 | if (foundSnippet) {
140 | self.writeJson(db, () => {
141 | res.json(self.createResponseObject(db, "Successfully deleted snippet with Id of "+id));
142 | });
143 | } else {
144 | self.catchError("Could not find snippet with an id of: " + id, res, 400);
145 | }
146 |
147 | }).catch(err => {
148 | self.catchError(err,res);
149 | });
150 | },
151 | returnJsonData: () => {
152 | return new Promise((response, reject) => {
153 | try {
154 |
155 | const database = require(path.resolve(__dirname, "../", self.jsonFilePath));
156 |
157 | // console.log("Got JSON DB. Last entry:");
158 | // console.log(database[database.length-1]._id);
159 | response(database);
160 | } catch (err) {
161 | reject(err);
162 | }
163 | });
164 | },
165 | createUniqueId: (db) => {
166 | let newId = Math.floor(Math.random() * 1000000);
167 | for(let i = 0; i < db.length; i++) {
168 | if(db[i]._id === newId) {
169 | return self.createUniqueId(db);
170 | }
171 | }
172 | return newId;
173 | },
174 | writeJson: (db, callback) => {
175 | let stream = fs.createWriteStream(path.resolve(__dirname, "../", self.jsonFilePath));
176 | stream.write(JSON.stringify(db), (err) => {
177 | if (err) {
178 | console.log("There was an error writing the file stream!");
179 | console.error(err);
180 | }
181 | stream.end();
182 | console.log("Wrote to file successfully!");
183 | callback();
184 | });
185 | // fs.writeFileSync(path.resolve(__dirname, "../", self.jsonFilePath), JSON.stringify(db), (err) => {
186 | // if (err) {
187 | // console.log("Error writing to JSON file!");
188 | // throw err;
189 | // }
190 | // });
191 | },
192 | createResponseObject: (db, successMsg) => {
193 | console.log(successMsg);
194 | return {
195 | error: false,
196 | responseMessage: successMsg,
197 | data: db,
198 | connected: true
199 | };
200 | },
201 | catchError: (err, res, status = 500) => {
202 | console.log("Caught Error: "+err);
203 | res.status(status).json({
204 | error:true,
205 | responseMessage: err,
206 | connected: true,
207 | data: []
208 | });
209 | },
210 |
211 |
212 | // for aliases
213 | getCategoryAliases: (req,res) => {
214 | console.log("Called getCategoryAliases!");
215 | self.aliasJsonFilePath = path.normalize(req.body.databaseUrl);
216 | console.log("alias json file path: " + self.aliasJsonFilePath);
217 |
218 | //create file if it doesn't exist.
219 | try {
220 |
221 | fs.open(self.aliasJsonFilePath, "wx", function (err, fd) {
222 | if (err) {
223 | // throw(err);
224 | console.log("categoryAliasesFile exists already");
225 | } else {
226 | let newJson = [
227 | {
228 | category: "category1",
229 | aliases: ["alias1","alias2","alias3","alias4"]
230 | }
231 | ];
232 | self.writeCategoryAliasesToJson(newJson, ()=> {
233 | fs.close(fd, function (err) {
234 | if (err) {
235 | console.log("error closing created json category aliases file: " + err);
236 | }
237 | });
238 | });
239 | }
240 |
241 | });
242 | } catch(err) {
243 | console.log("Error: Thrown error");
244 | console.log(err);
245 | }
246 |
247 | self.returnJsonCategoryAliasesData().then(data => {
248 | console.log("my alias data: ");
249 | console.log(data);
250 | let returnObj = {
251 | responseMessage: "Connected to JSON category aliases file and Returned Results!",
252 | connected: true,
253 | data: data
254 | };
255 | res.json(returnObj);
256 | }).catch(err => {
257 | res.status(500).json({
258 | error: true,
259 | responseMessage: "Sorry, could not connect to JSON category aliases file",
260 | connected: false,
261 | data: []
262 | });
263 | });
264 | },
265 | addCategoryAliases: (req,res) => {
266 | console.log("calling addCategoryAliases");
267 | const category = req.body.category;
268 | const aliases = req.body.aliases;
269 |
270 |
271 | self.returnJsonCategoryAliasesData().then(data => {
272 | console.log("promise return from addCategoryAliases");
273 | let db = data;
274 | const categoryAliasesObj = {
275 | _id: self.createUniqueCategoryAliasId(db),
276 | category: category,
277 | aliases: aliases
278 | };
279 |
280 | db.push(categoryAliasesObj);
281 |
282 | self.writeCategoryAliasesToJson(db, () => {
283 | console.log("Added category and aliases:");
284 | console.log(category);
285 | console.log(aliases);
286 | res.json(self.createResponseCategoryAliasesObject(db, "Added category and aliases to the Database Successfully!"));
287 | });
288 |
289 | }).catch(err => {
290 | let returnObj = {
291 | error: true,
292 | responseMessage: err,
293 | data: [],
294 | connected: true
295 | }
296 | res.status(500).json(returnObj);
297 | });
298 | },
299 | editCategoryAliases: (req,res) => {
300 | const id = req.body.id;
301 | const category = req.body.category;
302 | const aliases = req.body.aliases;
303 | const categoryAliasesObj = {
304 | category: category,
305 | aliases: aliases
306 | };
307 |
308 | self.returnJsonCategoryAliasesData().then(data => {
309 | let db = data;
310 | let foundCategory = false;
311 |
312 | for(let i = 0; i < db.length; i++) {
313 | if (db[i]._id === id) {
314 | db[i].category = category;
315 | db[i].aliases = aliases;
316 | foundCategory = true;
317 | }
318 | }
319 |
320 | if (foundCategory) {
321 | self.writeCategoryAliasesToJson(db, ()=> {
322 | res.json(self.createResponseCategoryAliasesObject(db, "Edited your category with an id of: " + id));
323 | });
324 | } else {
325 | self.catchCategoryAliasesError("Could not find category with an id of: " + id, res, 400);
326 | }
327 |
328 | }).catch((err) => {
329 | self.catchCategoryAliasesError(err, res);
330 | });
331 | },
332 | deleteCategoryAliases: (req,res) => {
333 | const id = req.body.id;
334 | self.returnJsonCategoryAliasesData().then(data => {
335 | let db = data;
336 | let foundCategory = false;
337 |
338 | for (let i = 0; i < db.length; i++) {
339 | if (db[i]._id === id) {
340 | //Splice this element out of the db.
341 | db.splice(i, 1);
342 | foundCategory = true;
343 | }
344 | }
345 |
346 | if (foundCategory) {
347 | self.writeCategoryAliasesToJson(db, () => {
348 | res.json(self.createResponseCategoryAliasesObject(db, "Successfully deleted category with Id of "+id));
349 | });
350 | } else {
351 | self.catchCategoryAliasesError("Could not find category with an id of: " + id, res, 400);
352 | }
353 |
354 | }).catch(err => {
355 | self.catchCategoryAliasesError(err,res);
356 | });
357 | },
358 | returnJsonCategoryAliasesData: () => {
359 | return new Promise((response, reject) => {
360 | try {
361 |
362 | const database = require(path.resolve(__dirname, "../", self.aliasJsonFilePath));
363 |
364 | // console.log("Got JSON DB. Last entry:");
365 | // console.log(database[database.length-1]._id);
366 | response(database);
367 | } catch (err) {
368 | reject(err);
369 | }
370 | });
371 | },
372 | createUniqueCategoryAliasId: (db) => {
373 | let newId = Math.floor(Math.random() * 1000000);
374 | for(let i = 0; i < db.length; i++) {
375 | if(db[i]._id === newId) {
376 | return self.createUniqueCategoryAliasId(db);
377 | }
378 | }
379 | return newId;
380 | },
381 | writeCategoryAliasesToJson: (db, callback) => {
382 | let stream = fs.createWriteStream(path.resolve(__dirname, "../", self.aliasJsonFilePath));
383 | stream.write(JSON.stringify(db), (err) => {
384 | if (err) {
385 | console.log("There was an error writing the file stream!");
386 | console.error(err);
387 | }
388 | stream.end();
389 | console.log("Wrote to file successfully!");
390 | callback();
391 | });
392 | // fs.writeFileSync(path.resolve(__dirname, "../", self.aliasJsonFilePath), JSON.stringify(db), (err) => {
393 | // if (err) {
394 | // console.log("Error writing to JSON file!");
395 | // throw err;
396 | // }
397 | // });
398 | },
399 | createResponseCategoryAliasesObject: (db, successMsg) => {
400 | console.log(successMsg);
401 | return {
402 | error: false,
403 | responseMessage: successMsg,
404 | data: db,
405 | connected: true
406 | };
407 | },
408 | catchCategoryAliasesError: (err, res, status = 500) => {
409 | console.log("Caught Error: "+err);
410 | res.status(status).json({
411 | error:true,
412 | responseMessage: err,
413 | connected: true,
414 | data: []
415 | });
416 | }
417 | }
--------------------------------------------------------------------------------
/dist/assets/sakura-dark.css:
--------------------------------------------------------------------------------
1 | /* $color-text: #dedce5; */
2 | /* Sakura.css v1.0.0
3 | * ================
4 | * Minimal css theme.
5 | * Project: https://github.com/oxalorg/sakura
6 | */
7 | /* Body */
8 | html {
9 | font-size: 62.5%;
10 | font-family: serif; }
11 |
12 | body {
13 | font-size: 1.8rem;
14 | line-height: 1.618;
15 | max-width: 38em;
16 | margin: auto;
17 | color: #c9c9c9;
18 | background-color: #222222;
19 | padding: 13px; }
20 |
21 | @media (max-width: 684px) {
22 | body {
23 | font-size: 1.53rem; } }
24 |
25 | @media (max-width: 382px) {
26 | body {
27 | font-size: 1.35rem; } }
28 |
29 | h1, h2, h3, h4, h5, h6 {
30 | line-height: 1.1;
31 | font-family: Verdana, Geneva, sans-serif;
32 | font-weight: 700;
33 | overflow-wrap: break-word;
34 | word-wrap: break-word;
35 | -ms-word-break: break-all;
36 | word-break: break-word;
37 | -ms-hyphens: auto;
38 | -moz-hyphens: auto;
39 | -webkit-hyphens: auto;
40 | hyphens: auto; }
41 |
42 | h1 {
43 | font-size: 2.35em; }
44 |
45 | h2 {
46 | font-size: 2.00em; }
47 |
48 | h3 {
49 | font-size: 1.75em; }
50 |
51 | h4 {
52 | font-size: 1.5em; }
53 |
54 | h5 {
55 | font-size: 1.25em; }
56 |
57 | h6 {
58 | font-size: 1em; }
59 |
60 | small, sub, sup {
61 | font-size: 75%; }
62 |
63 | hr {
64 | border-color: #ffffff; }
65 |
66 | a {
67 | text-decoration: none;
68 | color: #ffffff; }
69 | a:hover {
70 | color: #c9c9c9;
71 | border-bottom: 2px solid #c9c9c9; }
72 |
73 | ul {
74 | padding-left: 1.4em; }
75 |
76 | li {
77 | margin-bottom: 0.4em; }
78 |
79 | blockquote {
80 | font-style: italic;
81 | margin-left: 1.5em;
82 | padding-left: 1em;
83 | border-left: 3px solid #ffffff; }
84 |
85 | img {
86 | max-width: 100%; }
87 |
88 | /* Pre and Code */
89 | pre {
90 | background-color: #4a4a4a;
91 | display: block;
92 | padding: 1em;
93 | overflow-x: auto; }
94 |
95 | code {
96 | font-size: 0.9em;
97 | padding: 0 0.5em;
98 | background-color: #4a4a4a;
99 | white-space: pre-wrap; }
100 |
101 | pre > code {
102 | padding: 0;
103 | background-color: transparent;
104 | white-space: pre; }
105 |
106 | /* Tables */
107 | table {
108 | text-align: justify;
109 | width: 100%;
110 | border-collapse: collapse; }
111 |
112 | td, th {
113 | padding: 0.5em;
114 | border-bottom: 1px solid #4a4a4a; }
115 |
116 | /* Buttons, forms and input */
117 | input, textarea {
118 | border: 1px solid #c9c9c9; }
119 | input:focus, textarea:focus {
120 | border: 1px solid #ffffff; }
121 |
122 | textarea {
123 | width: 100%; }
124 |
125 | .button, button, input[type="submit"], input[type="reset"], input[type="button"] {
126 | display: inline-block;
127 | padding: 5px 10px;
128 | text-align: center;
129 | text-decoration: none;
130 | white-space: nowrap;
131 | background-color: #ffffff;
132 | color: #222222;
133 | border-radius: 1px;
134 | border: 1px solid #ffffff;
135 | cursor: pointer;
136 | box-sizing: border-box; }
137 | .button[disabled], button[disabled], input[type="submit"][disabled], input[type="reset"][disabled], input[type="button"][disabled] {
138 | cursor: default;
139 | opacity: .5; }
140 | .button:focus, .button:hover, button:focus, button:hover, input[type="submit"]:focus, input[type="submit"]:hover, input[type="reset"]:focus, input[type="reset"]:hover, input[type="button"]:focus, input[type="button"]:hover {
141 | background-color: #c9c9c9;
142 | border-color: #c9c9c9;
143 | color: #222222;
144 | outline: 0; }
145 |
146 | textarea, select, input[type] {
147 | color: #c9c9c9;
148 | padding: 6px 10px;
149 | /* The 6px vertically centers text on FF, ignored by Webkit */
150 | margin-bottom: 10px;
151 | background-color: #4a4a4a;
152 | border: 1px solid #4a4a4a;
153 | border-radius: 4px;
154 | box-shadow: none;
155 | box-sizing: border-box; }
156 | textarea:focus, select:focus, input[type]:focus {
157 | border: 1px solid #ffffff;
158 | outline: 0; }
159 |
160 | input[type="checkbox"]:focus {
161 | outline: 1px dotted #ffffff; }
162 |
163 | label, legend, fieldset {
164 | display: block;
165 | margin-bottom: .5rem;
166 | font-weight: 600; }
167 |
--------------------------------------------------------------------------------
/dist/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattcheah/cover-letter-snippets/c9d863fe638cdd6c997751fd59f45331883979b2/dist/favicon.ico
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |