├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
├── images
│ ├── paypal_dumbdumbdev.png
│ └── venmo_Oscar-M.png
├── index.html
└── robots.txt
├── server
├── .gitignore
├── index.js
├── package-lock.json
└── package.json
└── src
├── App.css
├── App.js
├── components
├── AddJob.js
├── Helper.js
├── ListJobs.js
├── NavBar.js
├── StatusCounts.js
└── UpdateJobs.js
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | server/job_appsDB.js
8 |
9 | # testing
10 | /coverage
11 |
12 | # production
13 | /build
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | https://github.com/Oscar6/app-tracker/assets/7444980/02cd4071-5488-40df-bd92-5f228ab88238
2 |
3 |
4 | # Setting up Job tracker
5 |
6 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
7 |
8 | ## Install PostgreSQL and clone project
9 |
10 |
11 | ### Setting up frontend
12 |
13 | Navigate into project and run:\
14 | `npm install`
15 |
16 | To start app, run:\
17 | `npm start`
18 |
19 |
20 | ### Setting up backend
21 |
22 | To connect database, make a file in your server folder that contains your DB properties:\
23 | Ex. jobs_db.js
24 |
25 | Apply the following properties with your DB info to the file:
26 |
27 | ```
28 | const Pool = require("pg").Pool;
29 | const pool = new Pool({
30 | user: "{username}",
31 | password: "{password}",
32 | host: "localhost",
33 | port: 5432,
34 | database: "{name_given_to_db}"
35 | });
36 |
37 | module.exports = pool;
38 | ```
39 |
40 | Navigate into server folder and run:\
41 | `npm install` OR `npm init` and step through default options
42 |
43 | Install Express, PG and CORS:\
44 | `npm i express pg cors`
45 |
46 | Install nodemon globally to automatically refresh server when there are changes to database:\
47 | `npm install -g nodemon`
48 |
49 | `server/index.js` contains server info and database calls.
50 |
51 | To start server, open a separate terminal and run:\
52 | `nodemon index`
53 |
54 |
55 | ### Setting up PostgreSQL
56 |
57 | Create and set database name and password
58 |
59 | Create table and run following script:
60 |
61 | ```
62 | CREATE TABLE companies (
63 | id integer NOT NULL UNIQUE,
64 | company_name varchar(50),
65 | job_role varchar(50),
66 | date_applied date,
67 | app_status varchar(50)
68 | );
69 | ```
70 |
71 | Verify calls on Postman
72 |
73 | ****** ****** ****** ******
74 | Send tacos if this app is helping
75 |
76 |
77 | [ ](https://account.venmo.com/u/Oscar-M) [ ](https://www.paypal.me/dumbdumbdev)
78 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "application-tracker",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "bootstrap-icons": "^1.10.3",
10 | "date-fns": "^2.29.3",
11 | "react": "^18.2.0",
12 | "react-bootstrap-typeahead": "^6.0.2",
13 | "react-dom": "^18.2.0",
14 | "react-scripts": "5.0.1",
15 | "validator": "^13.9.0",
16 | "web-vitals": "^2.1.4"
17 | },
18 | "scripts": {
19 | "start": "react-scripts start",
20 | "build": "react-scripts build",
21 | "test": "react-scripts test",
22 | "eject": "react-scripts eject"
23 | },
24 | "eslintConfig": {
25 | "extends": [
26 | "react-app",
27 | "react-app/jest"
28 | ]
29 | },
30 | "browserslist": {
31 | "production": [
32 | ">0.2%",
33 | "not dead",
34 | "not op_mini all"
35 | ],
36 | "development": [
37 | "last 1 chrome version",
38 | "last 1 firefox version",
39 | "last 1 safari version"
40 | ]
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/public/images/paypal_dumbdumbdev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Oscar6/app-tracker/93d4f103e6aa6c1d6598d32e2752ba8721700758/public/images/paypal_dumbdumbdev.png
--------------------------------------------------------------------------------
/public/images/venmo_Oscar-M.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Oscar6/app-tracker/93d4f103e6aa6c1d6598d32e2752ba8721700758/public/images/venmo_Oscar-M.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
16 |
17 | Job Tracker
18 |
19 |
20 |
21 |
22 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/server/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const helmet = require("helmet");
3 | const app = express();
4 | const cors = require("cors");
5 | const pool = require("./job_appsDB");
6 |
7 | // Middleware
8 | app.use(helmet());
9 | app.use(cors());
10 | app.use(express.json());
11 |
12 | // Routes
13 |
14 | // Create
15 | app.post("/job", async (req, res) => {
16 | try {
17 | const { company_name, job_role, job_link, job_salary, date_applied, app_status } = req.body;
18 | const newJob = await pool.query(
19 | "INSERT INTO companies (company_name, job_role, job_link, job_salary, date_applied, app_status) VALUES($1, $2, $3, $4, $5, $6) RETURNING *",
20 | [company_name, job_role, job_link, job_salary, date_applied, app_status]
21 | );
22 | res.status(201).json(newJob.rows[0]);
23 | } catch (err) {
24 | console.error(err.message);
25 | res.status(500).send("Server error");
26 | }
27 | console.log(req.body);
28 | });
29 |
30 | // Get All
31 | app.get("/job", async(req, res) => {
32 | try {
33 | const allJobs = await pool.query(`SELECT * FROM companies`);
34 | res.json(allJobs.rows)
35 | } catch (err) {
36 | console.log(err.message);
37 | }
38 | });
39 |
40 | // Get a job app
41 | app.get("/job/:id", async (req, res) => {
42 | try {
43 | const { id } = req.params;
44 | const jobApp = await pool.query(`SELECT * FROM companies WHERE id = $1`, [id]);
45 |
46 | res.json(jobApp.rows[0]);
47 | } catch (error) {
48 | console.log(err.message);
49 | }
50 | })
51 |
52 | // Update
53 | app.put("/job/:id", (req, res) => {
54 | const jobId = req.params.id;
55 | const updateJob = `UPDATE companies SET
56 | company_name = $1,
57 | job_role = $2,
58 | date_applied = $3,
59 | app_status = $4,
60 | status_rejected = $5,
61 | status_interviewed = $6,
62 | status_technical = $7,
63 | status_offer = $8,
64 | job_link = $9,
65 | job_salary = $10
66 | WHERE id = $11`;
67 | const values = [
68 | req.body.company_name,
69 | req.body.job_role,
70 | req.body.date_applied,
71 | req.body.app_status,
72 | req.body.status_rejected,
73 | req.body.status_interviewed,
74 | req.body.status_technical,
75 | req.body.status_offer,
76 | req.body.job_link,
77 | req.body.job_salary
78 | ];
79 |
80 | pool.query(updateJob, [...values, jobId]).then((response) => {
81 | console.log("Updated application");
82 | // console.log(response);
83 | res.send("Job application updated: " + JSON.stringify(req.body));
84 | }).catch((err) => {
85 | console.error(err);
86 | res.status(500).send("Error updating job application.");
87 | });
88 | console.log(req.body);
89 | });
90 |
91 | // Delete
92 | app.delete("/job/:id", async (req, res) => {
93 | try {
94 | const { id } = req.params;
95 | const deleteJob = await pool.query(`DELETE FROM companies WHERE id = $1`, [id]);
96 | res.json("Job has been deleted.");
97 | } catch (error) {
98 | console.log(err.message)
99 | }
100 | })
101 |
102 |
103 | app.listen(5000, () => {
104 | console.log("server started on port 5000");
105 | });
--------------------------------------------------------------------------------
/server/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "server",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "dependencies": {
12 | "cors": "^2.8.5",
13 | "express": "^4.18.2",
14 | "helmet": "^6.1.5",
15 | "pg": "^8.9.0"
16 | }
17 | },
18 | "node_modules/accepts": {
19 | "version": "1.3.8",
20 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
21 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
22 | "dependencies": {
23 | "mime-types": "~2.1.34",
24 | "negotiator": "0.6.3"
25 | },
26 | "engines": {
27 | "node": ">= 0.6"
28 | }
29 | },
30 | "node_modules/array-flatten": {
31 | "version": "1.1.1",
32 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
33 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
34 | },
35 | "node_modules/body-parser": {
36 | "version": "1.20.1",
37 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
38 | "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
39 | "dependencies": {
40 | "bytes": "3.1.2",
41 | "content-type": "~1.0.4",
42 | "debug": "2.6.9",
43 | "depd": "2.0.0",
44 | "destroy": "1.2.0",
45 | "http-errors": "2.0.0",
46 | "iconv-lite": "0.4.24",
47 | "on-finished": "2.4.1",
48 | "qs": "6.11.0",
49 | "raw-body": "2.5.1",
50 | "type-is": "~1.6.18",
51 | "unpipe": "1.0.0"
52 | },
53 | "engines": {
54 | "node": ">= 0.8",
55 | "npm": "1.2.8000 || >= 1.4.16"
56 | }
57 | },
58 | "node_modules/buffer-writer": {
59 | "version": "2.0.0",
60 | "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
61 | "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
62 | "engines": {
63 | "node": ">=4"
64 | }
65 | },
66 | "node_modules/bytes": {
67 | "version": "3.1.2",
68 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
69 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
70 | "engines": {
71 | "node": ">= 0.8"
72 | }
73 | },
74 | "node_modules/call-bind": {
75 | "version": "1.0.2",
76 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
77 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
78 | "dependencies": {
79 | "function-bind": "^1.1.1",
80 | "get-intrinsic": "^1.0.2"
81 | },
82 | "funding": {
83 | "url": "https://github.com/sponsors/ljharb"
84 | }
85 | },
86 | "node_modules/content-disposition": {
87 | "version": "0.5.4",
88 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
89 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
90 | "dependencies": {
91 | "safe-buffer": "5.2.1"
92 | },
93 | "engines": {
94 | "node": ">= 0.6"
95 | }
96 | },
97 | "node_modules/content-type": {
98 | "version": "1.0.5",
99 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
100 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
101 | "engines": {
102 | "node": ">= 0.6"
103 | }
104 | },
105 | "node_modules/cookie": {
106 | "version": "0.5.0",
107 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
108 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
109 | "engines": {
110 | "node": ">= 0.6"
111 | }
112 | },
113 | "node_modules/cookie-signature": {
114 | "version": "1.0.6",
115 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
116 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
117 | },
118 | "node_modules/cors": {
119 | "version": "2.8.5",
120 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
121 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
122 | "dependencies": {
123 | "object-assign": "^4",
124 | "vary": "^1"
125 | },
126 | "engines": {
127 | "node": ">= 0.10"
128 | }
129 | },
130 | "node_modules/debug": {
131 | "version": "2.6.9",
132 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
133 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
134 | "dependencies": {
135 | "ms": "2.0.0"
136 | }
137 | },
138 | "node_modules/depd": {
139 | "version": "2.0.0",
140 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
141 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
142 | "engines": {
143 | "node": ">= 0.8"
144 | }
145 | },
146 | "node_modules/destroy": {
147 | "version": "1.2.0",
148 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
149 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
150 | "engines": {
151 | "node": ">= 0.8",
152 | "npm": "1.2.8000 || >= 1.4.16"
153 | }
154 | },
155 | "node_modules/ee-first": {
156 | "version": "1.1.1",
157 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
158 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
159 | },
160 | "node_modules/encodeurl": {
161 | "version": "1.0.2",
162 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
163 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
164 | "engines": {
165 | "node": ">= 0.8"
166 | }
167 | },
168 | "node_modules/escape-html": {
169 | "version": "1.0.3",
170 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
171 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
172 | },
173 | "node_modules/etag": {
174 | "version": "1.8.1",
175 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
176 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
177 | "engines": {
178 | "node": ">= 0.6"
179 | }
180 | },
181 | "node_modules/express": {
182 | "version": "4.18.2",
183 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
184 | "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
185 | "dependencies": {
186 | "accepts": "~1.3.8",
187 | "array-flatten": "1.1.1",
188 | "body-parser": "1.20.1",
189 | "content-disposition": "0.5.4",
190 | "content-type": "~1.0.4",
191 | "cookie": "0.5.0",
192 | "cookie-signature": "1.0.6",
193 | "debug": "2.6.9",
194 | "depd": "2.0.0",
195 | "encodeurl": "~1.0.2",
196 | "escape-html": "~1.0.3",
197 | "etag": "~1.8.1",
198 | "finalhandler": "1.2.0",
199 | "fresh": "0.5.2",
200 | "http-errors": "2.0.0",
201 | "merge-descriptors": "1.0.1",
202 | "methods": "~1.1.2",
203 | "on-finished": "2.4.1",
204 | "parseurl": "~1.3.3",
205 | "path-to-regexp": "0.1.7",
206 | "proxy-addr": "~2.0.7",
207 | "qs": "6.11.0",
208 | "range-parser": "~1.2.1",
209 | "safe-buffer": "5.2.1",
210 | "send": "0.18.0",
211 | "serve-static": "1.15.0",
212 | "setprototypeof": "1.2.0",
213 | "statuses": "2.0.1",
214 | "type-is": "~1.6.18",
215 | "utils-merge": "1.0.1",
216 | "vary": "~1.1.2"
217 | },
218 | "engines": {
219 | "node": ">= 0.10.0"
220 | }
221 | },
222 | "node_modules/finalhandler": {
223 | "version": "1.2.0",
224 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
225 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
226 | "dependencies": {
227 | "debug": "2.6.9",
228 | "encodeurl": "~1.0.2",
229 | "escape-html": "~1.0.3",
230 | "on-finished": "2.4.1",
231 | "parseurl": "~1.3.3",
232 | "statuses": "2.0.1",
233 | "unpipe": "~1.0.0"
234 | },
235 | "engines": {
236 | "node": ">= 0.8"
237 | }
238 | },
239 | "node_modules/forwarded": {
240 | "version": "0.2.0",
241 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
242 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
243 | "engines": {
244 | "node": ">= 0.6"
245 | }
246 | },
247 | "node_modules/fresh": {
248 | "version": "0.5.2",
249 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
250 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
251 | "engines": {
252 | "node": ">= 0.6"
253 | }
254 | },
255 | "node_modules/function-bind": {
256 | "version": "1.1.1",
257 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
258 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
259 | },
260 | "node_modules/get-intrinsic": {
261 | "version": "1.2.0",
262 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
263 | "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
264 | "dependencies": {
265 | "function-bind": "^1.1.1",
266 | "has": "^1.0.3",
267 | "has-symbols": "^1.0.3"
268 | },
269 | "funding": {
270 | "url": "https://github.com/sponsors/ljharb"
271 | }
272 | },
273 | "node_modules/has": {
274 | "version": "1.0.3",
275 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
276 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
277 | "dependencies": {
278 | "function-bind": "^1.1.1"
279 | },
280 | "engines": {
281 | "node": ">= 0.4.0"
282 | }
283 | },
284 | "node_modules/has-symbols": {
285 | "version": "1.0.3",
286 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
287 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
288 | "engines": {
289 | "node": ">= 0.4"
290 | },
291 | "funding": {
292 | "url": "https://github.com/sponsors/ljharb"
293 | }
294 | },
295 | "node_modules/helmet": {
296 | "version": "6.1.5",
297 | "resolved": "https://registry.npmjs.org/helmet/-/helmet-6.1.5.tgz",
298 | "integrity": "sha512-UgAvdoG0BhF9vcCh/j0bWtElo2ZHHk6OzC98NLCM6zK03DEVSM0vUAtT7iR+oTo2Mi6sGelAH3tL6B/uUWxV4g==",
299 | "engines": {
300 | "node": ">=14.0.0"
301 | }
302 | },
303 | "node_modules/http-errors": {
304 | "version": "2.0.0",
305 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
306 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
307 | "dependencies": {
308 | "depd": "2.0.0",
309 | "inherits": "2.0.4",
310 | "setprototypeof": "1.2.0",
311 | "statuses": "2.0.1",
312 | "toidentifier": "1.0.1"
313 | },
314 | "engines": {
315 | "node": ">= 0.8"
316 | }
317 | },
318 | "node_modules/iconv-lite": {
319 | "version": "0.4.24",
320 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
321 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
322 | "dependencies": {
323 | "safer-buffer": ">= 2.1.2 < 3"
324 | },
325 | "engines": {
326 | "node": ">=0.10.0"
327 | }
328 | },
329 | "node_modules/inherits": {
330 | "version": "2.0.4",
331 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
332 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
333 | },
334 | "node_modules/ipaddr.js": {
335 | "version": "1.9.1",
336 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
337 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
338 | "engines": {
339 | "node": ">= 0.10"
340 | }
341 | },
342 | "node_modules/media-typer": {
343 | "version": "0.3.0",
344 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
345 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
346 | "engines": {
347 | "node": ">= 0.6"
348 | }
349 | },
350 | "node_modules/merge-descriptors": {
351 | "version": "1.0.1",
352 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
353 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
354 | },
355 | "node_modules/methods": {
356 | "version": "1.1.2",
357 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
358 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
359 | "engines": {
360 | "node": ">= 0.6"
361 | }
362 | },
363 | "node_modules/mime": {
364 | "version": "1.6.0",
365 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
366 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
367 | "bin": {
368 | "mime": "cli.js"
369 | },
370 | "engines": {
371 | "node": ">=4"
372 | }
373 | },
374 | "node_modules/mime-db": {
375 | "version": "1.52.0",
376 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
377 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
378 | "engines": {
379 | "node": ">= 0.6"
380 | }
381 | },
382 | "node_modules/mime-types": {
383 | "version": "2.1.35",
384 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
385 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
386 | "dependencies": {
387 | "mime-db": "1.52.0"
388 | },
389 | "engines": {
390 | "node": ">= 0.6"
391 | }
392 | },
393 | "node_modules/ms": {
394 | "version": "2.0.0",
395 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
396 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
397 | },
398 | "node_modules/negotiator": {
399 | "version": "0.6.3",
400 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
401 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
402 | "engines": {
403 | "node": ">= 0.6"
404 | }
405 | },
406 | "node_modules/object-assign": {
407 | "version": "4.1.1",
408 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
409 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
410 | "engines": {
411 | "node": ">=0.10.0"
412 | }
413 | },
414 | "node_modules/object-inspect": {
415 | "version": "1.12.3",
416 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
417 | "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
418 | "funding": {
419 | "url": "https://github.com/sponsors/ljharb"
420 | }
421 | },
422 | "node_modules/on-finished": {
423 | "version": "2.4.1",
424 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
425 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
426 | "dependencies": {
427 | "ee-first": "1.1.1"
428 | },
429 | "engines": {
430 | "node": ">= 0.8"
431 | }
432 | },
433 | "node_modules/packet-reader": {
434 | "version": "1.0.0",
435 | "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
436 | "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
437 | },
438 | "node_modules/parseurl": {
439 | "version": "1.3.3",
440 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
441 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
442 | "engines": {
443 | "node": ">= 0.8"
444 | }
445 | },
446 | "node_modules/path-to-regexp": {
447 | "version": "0.1.7",
448 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
449 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
450 | },
451 | "node_modules/pg": {
452 | "version": "8.9.0",
453 | "resolved": "https://registry.npmjs.org/pg/-/pg-8.9.0.tgz",
454 | "integrity": "sha512-ZJM+qkEbtOHRuXjmvBtOgNOXOtLSbxiMiUVMgE4rV6Zwocy03RicCVvDXgx8l4Biwo8/qORUnEqn2fdQzV7KCg==",
455 | "dependencies": {
456 | "buffer-writer": "2.0.0",
457 | "packet-reader": "1.0.0",
458 | "pg-connection-string": "^2.5.0",
459 | "pg-pool": "^3.5.2",
460 | "pg-protocol": "^1.6.0",
461 | "pg-types": "^2.1.0",
462 | "pgpass": "1.x"
463 | },
464 | "engines": {
465 | "node": ">= 8.0.0"
466 | },
467 | "peerDependencies": {
468 | "pg-native": ">=3.0.1"
469 | },
470 | "peerDependenciesMeta": {
471 | "pg-native": {
472 | "optional": true
473 | }
474 | }
475 | },
476 | "node_modules/pg-connection-string": {
477 | "version": "2.5.0",
478 | "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz",
479 | "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ=="
480 | },
481 | "node_modules/pg-int8": {
482 | "version": "1.0.1",
483 | "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
484 | "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
485 | "engines": {
486 | "node": ">=4.0.0"
487 | }
488 | },
489 | "node_modules/pg-pool": {
490 | "version": "3.5.2",
491 | "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz",
492 | "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==",
493 | "peerDependencies": {
494 | "pg": ">=8.0"
495 | }
496 | },
497 | "node_modules/pg-protocol": {
498 | "version": "1.6.0",
499 | "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
500 | "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
501 | },
502 | "node_modules/pg-types": {
503 | "version": "2.2.0",
504 | "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
505 | "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
506 | "dependencies": {
507 | "pg-int8": "1.0.1",
508 | "postgres-array": "~2.0.0",
509 | "postgres-bytea": "~1.0.0",
510 | "postgres-date": "~1.0.4",
511 | "postgres-interval": "^1.1.0"
512 | },
513 | "engines": {
514 | "node": ">=4"
515 | }
516 | },
517 | "node_modules/pgpass": {
518 | "version": "1.0.5",
519 | "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
520 | "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
521 | "dependencies": {
522 | "split2": "^4.1.0"
523 | }
524 | },
525 | "node_modules/postgres-array": {
526 | "version": "2.0.0",
527 | "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
528 | "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
529 | "engines": {
530 | "node": ">=4"
531 | }
532 | },
533 | "node_modules/postgres-bytea": {
534 | "version": "1.0.0",
535 | "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
536 | "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
537 | "engines": {
538 | "node": ">=0.10.0"
539 | }
540 | },
541 | "node_modules/postgres-date": {
542 | "version": "1.0.7",
543 | "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
544 | "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
545 | "engines": {
546 | "node": ">=0.10.0"
547 | }
548 | },
549 | "node_modules/postgres-interval": {
550 | "version": "1.2.0",
551 | "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
552 | "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
553 | "dependencies": {
554 | "xtend": "^4.0.0"
555 | },
556 | "engines": {
557 | "node": ">=0.10.0"
558 | }
559 | },
560 | "node_modules/proxy-addr": {
561 | "version": "2.0.7",
562 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
563 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
564 | "dependencies": {
565 | "forwarded": "0.2.0",
566 | "ipaddr.js": "1.9.1"
567 | },
568 | "engines": {
569 | "node": ">= 0.10"
570 | }
571 | },
572 | "node_modules/qs": {
573 | "version": "6.11.0",
574 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
575 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
576 | "dependencies": {
577 | "side-channel": "^1.0.4"
578 | },
579 | "engines": {
580 | "node": ">=0.6"
581 | },
582 | "funding": {
583 | "url": "https://github.com/sponsors/ljharb"
584 | }
585 | },
586 | "node_modules/range-parser": {
587 | "version": "1.2.1",
588 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
589 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
590 | "engines": {
591 | "node": ">= 0.6"
592 | }
593 | },
594 | "node_modules/raw-body": {
595 | "version": "2.5.1",
596 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
597 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
598 | "dependencies": {
599 | "bytes": "3.1.2",
600 | "http-errors": "2.0.0",
601 | "iconv-lite": "0.4.24",
602 | "unpipe": "1.0.0"
603 | },
604 | "engines": {
605 | "node": ">= 0.8"
606 | }
607 | },
608 | "node_modules/safe-buffer": {
609 | "version": "5.2.1",
610 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
611 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
612 | "funding": [
613 | {
614 | "type": "github",
615 | "url": "https://github.com/sponsors/feross"
616 | },
617 | {
618 | "type": "patreon",
619 | "url": "https://www.patreon.com/feross"
620 | },
621 | {
622 | "type": "consulting",
623 | "url": "https://feross.org/support"
624 | }
625 | ]
626 | },
627 | "node_modules/safer-buffer": {
628 | "version": "2.1.2",
629 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
630 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
631 | },
632 | "node_modules/send": {
633 | "version": "0.18.0",
634 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
635 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
636 | "dependencies": {
637 | "debug": "2.6.9",
638 | "depd": "2.0.0",
639 | "destroy": "1.2.0",
640 | "encodeurl": "~1.0.2",
641 | "escape-html": "~1.0.3",
642 | "etag": "~1.8.1",
643 | "fresh": "0.5.2",
644 | "http-errors": "2.0.0",
645 | "mime": "1.6.0",
646 | "ms": "2.1.3",
647 | "on-finished": "2.4.1",
648 | "range-parser": "~1.2.1",
649 | "statuses": "2.0.1"
650 | },
651 | "engines": {
652 | "node": ">= 0.8.0"
653 | }
654 | },
655 | "node_modules/send/node_modules/ms": {
656 | "version": "2.1.3",
657 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
658 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
659 | },
660 | "node_modules/serve-static": {
661 | "version": "1.15.0",
662 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
663 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
664 | "dependencies": {
665 | "encodeurl": "~1.0.2",
666 | "escape-html": "~1.0.3",
667 | "parseurl": "~1.3.3",
668 | "send": "0.18.0"
669 | },
670 | "engines": {
671 | "node": ">= 0.8.0"
672 | }
673 | },
674 | "node_modules/setprototypeof": {
675 | "version": "1.2.0",
676 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
677 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
678 | },
679 | "node_modules/side-channel": {
680 | "version": "1.0.4",
681 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
682 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
683 | "dependencies": {
684 | "call-bind": "^1.0.0",
685 | "get-intrinsic": "^1.0.2",
686 | "object-inspect": "^1.9.0"
687 | },
688 | "funding": {
689 | "url": "https://github.com/sponsors/ljharb"
690 | }
691 | },
692 | "node_modules/split2": {
693 | "version": "4.1.0",
694 | "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz",
695 | "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==",
696 | "engines": {
697 | "node": ">= 10.x"
698 | }
699 | },
700 | "node_modules/statuses": {
701 | "version": "2.0.1",
702 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
703 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
704 | "engines": {
705 | "node": ">= 0.8"
706 | }
707 | },
708 | "node_modules/toidentifier": {
709 | "version": "1.0.1",
710 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
711 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
712 | "engines": {
713 | "node": ">=0.6"
714 | }
715 | },
716 | "node_modules/type-is": {
717 | "version": "1.6.18",
718 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
719 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
720 | "dependencies": {
721 | "media-typer": "0.3.0",
722 | "mime-types": "~2.1.24"
723 | },
724 | "engines": {
725 | "node": ">= 0.6"
726 | }
727 | },
728 | "node_modules/unpipe": {
729 | "version": "1.0.0",
730 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
731 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
732 | "engines": {
733 | "node": ">= 0.8"
734 | }
735 | },
736 | "node_modules/utils-merge": {
737 | "version": "1.0.1",
738 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
739 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
740 | "engines": {
741 | "node": ">= 0.4.0"
742 | }
743 | },
744 | "node_modules/vary": {
745 | "version": "1.1.2",
746 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
747 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
748 | "engines": {
749 | "node": ">= 0.8"
750 | }
751 | },
752 | "node_modules/xtend": {
753 | "version": "4.0.2",
754 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
755 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
756 | "engines": {
757 | "node": ">=0.4"
758 | }
759 | }
760 | },
761 | "dependencies": {
762 | "accepts": {
763 | "version": "1.3.8",
764 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
765 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
766 | "requires": {
767 | "mime-types": "~2.1.34",
768 | "negotiator": "0.6.3"
769 | }
770 | },
771 | "array-flatten": {
772 | "version": "1.1.1",
773 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
774 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
775 | },
776 | "body-parser": {
777 | "version": "1.20.1",
778 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
779 | "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
780 | "requires": {
781 | "bytes": "3.1.2",
782 | "content-type": "~1.0.4",
783 | "debug": "2.6.9",
784 | "depd": "2.0.0",
785 | "destroy": "1.2.0",
786 | "http-errors": "2.0.0",
787 | "iconv-lite": "0.4.24",
788 | "on-finished": "2.4.1",
789 | "qs": "6.11.0",
790 | "raw-body": "2.5.1",
791 | "type-is": "~1.6.18",
792 | "unpipe": "1.0.0"
793 | }
794 | },
795 | "buffer-writer": {
796 | "version": "2.0.0",
797 | "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
798 | "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw=="
799 | },
800 | "bytes": {
801 | "version": "3.1.2",
802 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
803 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
804 | },
805 | "call-bind": {
806 | "version": "1.0.2",
807 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
808 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
809 | "requires": {
810 | "function-bind": "^1.1.1",
811 | "get-intrinsic": "^1.0.2"
812 | }
813 | },
814 | "content-disposition": {
815 | "version": "0.5.4",
816 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
817 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
818 | "requires": {
819 | "safe-buffer": "5.2.1"
820 | }
821 | },
822 | "content-type": {
823 | "version": "1.0.5",
824 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
825 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
826 | },
827 | "cookie": {
828 | "version": "0.5.0",
829 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
830 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
831 | },
832 | "cookie-signature": {
833 | "version": "1.0.6",
834 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
835 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
836 | },
837 | "cors": {
838 | "version": "2.8.5",
839 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
840 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
841 | "requires": {
842 | "object-assign": "^4",
843 | "vary": "^1"
844 | }
845 | },
846 | "debug": {
847 | "version": "2.6.9",
848 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
849 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
850 | "requires": {
851 | "ms": "2.0.0"
852 | }
853 | },
854 | "depd": {
855 | "version": "2.0.0",
856 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
857 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
858 | },
859 | "destroy": {
860 | "version": "1.2.0",
861 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
862 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
863 | },
864 | "ee-first": {
865 | "version": "1.1.1",
866 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
867 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
868 | },
869 | "encodeurl": {
870 | "version": "1.0.2",
871 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
872 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
873 | },
874 | "escape-html": {
875 | "version": "1.0.3",
876 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
877 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
878 | },
879 | "etag": {
880 | "version": "1.8.1",
881 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
882 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
883 | },
884 | "express": {
885 | "version": "4.18.2",
886 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
887 | "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
888 | "requires": {
889 | "accepts": "~1.3.8",
890 | "array-flatten": "1.1.1",
891 | "body-parser": "1.20.1",
892 | "content-disposition": "0.5.4",
893 | "content-type": "~1.0.4",
894 | "cookie": "0.5.0",
895 | "cookie-signature": "1.0.6",
896 | "debug": "2.6.9",
897 | "depd": "2.0.0",
898 | "encodeurl": "~1.0.2",
899 | "escape-html": "~1.0.3",
900 | "etag": "~1.8.1",
901 | "finalhandler": "1.2.0",
902 | "fresh": "0.5.2",
903 | "http-errors": "2.0.0",
904 | "merge-descriptors": "1.0.1",
905 | "methods": "~1.1.2",
906 | "on-finished": "2.4.1",
907 | "parseurl": "~1.3.3",
908 | "path-to-regexp": "0.1.7",
909 | "proxy-addr": "~2.0.7",
910 | "qs": "6.11.0",
911 | "range-parser": "~1.2.1",
912 | "safe-buffer": "5.2.1",
913 | "send": "0.18.0",
914 | "serve-static": "1.15.0",
915 | "setprototypeof": "1.2.0",
916 | "statuses": "2.0.1",
917 | "type-is": "~1.6.18",
918 | "utils-merge": "1.0.1",
919 | "vary": "~1.1.2"
920 | }
921 | },
922 | "finalhandler": {
923 | "version": "1.2.0",
924 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
925 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
926 | "requires": {
927 | "debug": "2.6.9",
928 | "encodeurl": "~1.0.2",
929 | "escape-html": "~1.0.3",
930 | "on-finished": "2.4.1",
931 | "parseurl": "~1.3.3",
932 | "statuses": "2.0.1",
933 | "unpipe": "~1.0.0"
934 | }
935 | },
936 | "forwarded": {
937 | "version": "0.2.0",
938 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
939 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
940 | },
941 | "fresh": {
942 | "version": "0.5.2",
943 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
944 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
945 | },
946 | "function-bind": {
947 | "version": "1.1.1",
948 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
949 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
950 | },
951 | "get-intrinsic": {
952 | "version": "1.2.0",
953 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
954 | "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
955 | "requires": {
956 | "function-bind": "^1.1.1",
957 | "has": "^1.0.3",
958 | "has-symbols": "^1.0.3"
959 | }
960 | },
961 | "has": {
962 | "version": "1.0.3",
963 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
964 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
965 | "requires": {
966 | "function-bind": "^1.1.1"
967 | }
968 | },
969 | "has-symbols": {
970 | "version": "1.0.3",
971 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
972 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
973 | },
974 | "helmet": {
975 | "version": "6.1.5",
976 | "resolved": "https://registry.npmjs.org/helmet/-/helmet-6.1.5.tgz",
977 | "integrity": "sha512-UgAvdoG0BhF9vcCh/j0bWtElo2ZHHk6OzC98NLCM6zK03DEVSM0vUAtT7iR+oTo2Mi6sGelAH3tL6B/uUWxV4g=="
978 | },
979 | "http-errors": {
980 | "version": "2.0.0",
981 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
982 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
983 | "requires": {
984 | "depd": "2.0.0",
985 | "inherits": "2.0.4",
986 | "setprototypeof": "1.2.0",
987 | "statuses": "2.0.1",
988 | "toidentifier": "1.0.1"
989 | }
990 | },
991 | "iconv-lite": {
992 | "version": "0.4.24",
993 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
994 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
995 | "requires": {
996 | "safer-buffer": ">= 2.1.2 < 3"
997 | }
998 | },
999 | "inherits": {
1000 | "version": "2.0.4",
1001 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
1002 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
1003 | },
1004 | "ipaddr.js": {
1005 | "version": "1.9.1",
1006 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
1007 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
1008 | },
1009 | "media-typer": {
1010 | "version": "0.3.0",
1011 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
1012 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
1013 | },
1014 | "merge-descriptors": {
1015 | "version": "1.0.1",
1016 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
1017 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
1018 | },
1019 | "methods": {
1020 | "version": "1.1.2",
1021 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
1022 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
1023 | },
1024 | "mime": {
1025 | "version": "1.6.0",
1026 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
1027 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
1028 | },
1029 | "mime-db": {
1030 | "version": "1.52.0",
1031 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
1032 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
1033 | },
1034 | "mime-types": {
1035 | "version": "2.1.35",
1036 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
1037 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
1038 | "requires": {
1039 | "mime-db": "1.52.0"
1040 | }
1041 | },
1042 | "ms": {
1043 | "version": "2.0.0",
1044 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1045 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
1046 | },
1047 | "negotiator": {
1048 | "version": "0.6.3",
1049 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
1050 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
1051 | },
1052 | "object-assign": {
1053 | "version": "4.1.1",
1054 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1055 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
1056 | },
1057 | "object-inspect": {
1058 | "version": "1.12.3",
1059 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
1060 | "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g=="
1061 | },
1062 | "on-finished": {
1063 | "version": "2.4.1",
1064 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
1065 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
1066 | "requires": {
1067 | "ee-first": "1.1.1"
1068 | }
1069 | },
1070 | "packet-reader": {
1071 | "version": "1.0.0",
1072 | "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
1073 | "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
1074 | },
1075 | "parseurl": {
1076 | "version": "1.3.3",
1077 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1078 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
1079 | },
1080 | "path-to-regexp": {
1081 | "version": "0.1.7",
1082 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
1083 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
1084 | },
1085 | "pg": {
1086 | "version": "8.9.0",
1087 | "resolved": "https://registry.npmjs.org/pg/-/pg-8.9.0.tgz",
1088 | "integrity": "sha512-ZJM+qkEbtOHRuXjmvBtOgNOXOtLSbxiMiUVMgE4rV6Zwocy03RicCVvDXgx8l4Biwo8/qORUnEqn2fdQzV7KCg==",
1089 | "requires": {
1090 | "buffer-writer": "2.0.0",
1091 | "packet-reader": "1.0.0",
1092 | "pg-connection-string": "^2.5.0",
1093 | "pg-pool": "^3.5.2",
1094 | "pg-protocol": "^1.6.0",
1095 | "pg-types": "^2.1.0",
1096 | "pgpass": "1.x"
1097 | }
1098 | },
1099 | "pg-connection-string": {
1100 | "version": "2.5.0",
1101 | "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz",
1102 | "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ=="
1103 | },
1104 | "pg-int8": {
1105 | "version": "1.0.1",
1106 | "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
1107 | "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
1108 | },
1109 | "pg-pool": {
1110 | "version": "3.5.2",
1111 | "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz",
1112 | "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==",
1113 | "requires": {}
1114 | },
1115 | "pg-protocol": {
1116 | "version": "1.6.0",
1117 | "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
1118 | "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
1119 | },
1120 | "pg-types": {
1121 | "version": "2.2.0",
1122 | "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
1123 | "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
1124 | "requires": {
1125 | "pg-int8": "1.0.1",
1126 | "postgres-array": "~2.0.0",
1127 | "postgres-bytea": "~1.0.0",
1128 | "postgres-date": "~1.0.4",
1129 | "postgres-interval": "^1.1.0"
1130 | }
1131 | },
1132 | "pgpass": {
1133 | "version": "1.0.5",
1134 | "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
1135 | "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
1136 | "requires": {
1137 | "split2": "^4.1.0"
1138 | }
1139 | },
1140 | "postgres-array": {
1141 | "version": "2.0.0",
1142 | "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
1143 | "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="
1144 | },
1145 | "postgres-bytea": {
1146 | "version": "1.0.0",
1147 | "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
1148 | "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="
1149 | },
1150 | "postgres-date": {
1151 | "version": "1.0.7",
1152 | "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
1153 | "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="
1154 | },
1155 | "postgres-interval": {
1156 | "version": "1.2.0",
1157 | "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
1158 | "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
1159 | "requires": {
1160 | "xtend": "^4.0.0"
1161 | }
1162 | },
1163 | "proxy-addr": {
1164 | "version": "2.0.7",
1165 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
1166 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
1167 | "requires": {
1168 | "forwarded": "0.2.0",
1169 | "ipaddr.js": "1.9.1"
1170 | }
1171 | },
1172 | "qs": {
1173 | "version": "6.11.0",
1174 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
1175 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
1176 | "requires": {
1177 | "side-channel": "^1.0.4"
1178 | }
1179 | },
1180 | "range-parser": {
1181 | "version": "1.2.1",
1182 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1183 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
1184 | },
1185 | "raw-body": {
1186 | "version": "2.5.1",
1187 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
1188 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
1189 | "requires": {
1190 | "bytes": "3.1.2",
1191 | "http-errors": "2.0.0",
1192 | "iconv-lite": "0.4.24",
1193 | "unpipe": "1.0.0"
1194 | }
1195 | },
1196 | "safe-buffer": {
1197 | "version": "5.2.1",
1198 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1199 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
1200 | },
1201 | "safer-buffer": {
1202 | "version": "2.1.2",
1203 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1204 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
1205 | },
1206 | "send": {
1207 | "version": "0.18.0",
1208 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
1209 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
1210 | "requires": {
1211 | "debug": "2.6.9",
1212 | "depd": "2.0.0",
1213 | "destroy": "1.2.0",
1214 | "encodeurl": "~1.0.2",
1215 | "escape-html": "~1.0.3",
1216 | "etag": "~1.8.1",
1217 | "fresh": "0.5.2",
1218 | "http-errors": "2.0.0",
1219 | "mime": "1.6.0",
1220 | "ms": "2.1.3",
1221 | "on-finished": "2.4.1",
1222 | "range-parser": "~1.2.1",
1223 | "statuses": "2.0.1"
1224 | },
1225 | "dependencies": {
1226 | "ms": {
1227 | "version": "2.1.3",
1228 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1229 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
1230 | }
1231 | }
1232 | },
1233 | "serve-static": {
1234 | "version": "1.15.0",
1235 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
1236 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
1237 | "requires": {
1238 | "encodeurl": "~1.0.2",
1239 | "escape-html": "~1.0.3",
1240 | "parseurl": "~1.3.3",
1241 | "send": "0.18.0"
1242 | }
1243 | },
1244 | "setprototypeof": {
1245 | "version": "1.2.0",
1246 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1247 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
1248 | },
1249 | "side-channel": {
1250 | "version": "1.0.4",
1251 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
1252 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
1253 | "requires": {
1254 | "call-bind": "^1.0.0",
1255 | "get-intrinsic": "^1.0.2",
1256 | "object-inspect": "^1.9.0"
1257 | }
1258 | },
1259 | "split2": {
1260 | "version": "4.1.0",
1261 | "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz",
1262 | "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ=="
1263 | },
1264 | "statuses": {
1265 | "version": "2.0.1",
1266 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
1267 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
1268 | },
1269 | "toidentifier": {
1270 | "version": "1.0.1",
1271 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
1272 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
1273 | },
1274 | "type-is": {
1275 | "version": "1.6.18",
1276 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1277 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1278 | "requires": {
1279 | "media-typer": "0.3.0",
1280 | "mime-types": "~2.1.24"
1281 | }
1282 | },
1283 | "unpipe": {
1284 | "version": "1.0.0",
1285 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1286 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
1287 | },
1288 | "utils-merge": {
1289 | "version": "1.0.1",
1290 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1291 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="
1292 | },
1293 | "vary": {
1294 | "version": "1.1.2",
1295 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1296 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
1297 | },
1298 | "xtend": {
1299 | "version": "4.0.2",
1300 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
1301 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
1302 | }
1303 | }
1304 | }
1305 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "artemis",
10 | "license": "ISC",
11 | "dependencies": {
12 | "cors": "^2.8.5",
13 | "express": "^4.18.2",
14 | "helmet": "^6.1.5",
15 | "pg": "^8.9.0"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: rgb(1, 34, 48);
3 | }
4 |
5 | .nav {
6 | height: 70px;
7 | display: flex;
8 | padding: 20px 36px;
9 | background-color: rgb(23, 93, 125);
10 | color: rgba(255, 255, 255, 0.914);
11 | box-shadow: 0px 2.98256px 20px rgba(0, 0, 0, 0.817);
12 | position: sticky;
13 | top: 0;
14 | z-index: 2;
15 | }
16 |
17 | .add-job {
18 | display: flex;
19 | align-items: flex-end;
20 | margin-left: auto;
21 | margin-bottom: 20px;
22 | }
23 |
24 | .sticky-count {
25 | padding: 15px;
26 | background-color: rgba(1, 34, 48, 0.97);
27 | position: sticky;
28 | top: 0;
29 | z-index: 1;
30 | }
31 |
32 | .list-header {
33 | display: flex;
34 | justify-content: center;
35 | padding-top: 50px;
36 | color: rgba(255, 255, 255, 0.914);
37 | }
38 |
39 | .status-counts {
40 | display: flex;
41 | justify-content: center;
42 | margin: 30px;
43 | color: rgba(255, 255, 255, 0.914);
44 | }
45 |
46 | .statuses {
47 | margin-right: 20px;
48 | font-size: 20px;
49 | }
50 |
51 | .job-filter {
52 | width: 250px;
53 | margin: 0 auto;
54 | display: flex;
55 | justify-content: center;
56 | }
57 |
58 | .job-filter .rbt-input-hint-container,
59 | .job-filter .rbt-input-main {
60 | width: 250px;
61 | display: flex;
62 | border: 1px;
63 | background-color: rgba(255, 255, 255, 0.914);
64 | border-style: solid;
65 | border-radius: 6px;
66 | box-shadow: 0px 2.98256px 10px rgba(0, 0, 0, 0.817);
67 | }
68 |
69 | .search-button {
70 | display: flex;
71 | margin-left: 5px;
72 | background-color: rgba(255, 255, 255, 0.914);
73 | }
74 |
75 | .clear-button {
76 | margin-left: 5px;
77 | background-color: rgba(255, 255, 255, 0.914);
78 | }
79 |
80 | .job-list {
81 | padding: 80px 200px;
82 | display: flex;
83 | flex-wrap: wrap;
84 | gap: 20px;
85 | overflow-x: auto;
86 | justify-content: center;
87 | }
88 |
89 | .job {
90 | background-color: rgb(23, 93, 125);
91 | color: rgba(255, 255, 255, 0.914);
92 | padding: 10px;
93 | height: 300px;
94 | width: 215px;
95 | font-size: 20px;
96 | flex: 0 0 auto;
97 | display: flex;
98 | flex-direction: column;
99 | position: relative;
100 | justify-content: space-between;
101 | border: 1px;
102 | border-color: rgb(23, 93, 125);
103 | border-style: solid;
104 | border-radius: 4px;
105 | box-shadow: 0px 2.98256px 10px rgba(0, 0, 0, 0.817);
106 | }
107 |
108 | .company-name {
109 | text-overflow: ellipsis;
110 | white-space: nowrap;
111 | overflow: hidden;
112 | color: inherit;
113 | }
114 |
115 | .job-role {
116 | text-overflow: ellipsis;
117 | white-space: nowrap;
118 | overflow: hidden;
119 | margin-bottom: 5px;
120 | }
121 |
122 | .job-salary {
123 | text-overflow: ellipsis;
124 | color: rgb(1, 34, 48);
125 | white-space: nowrap;
126 | overflow: hidden;
127 | margin-bottom: 5px;
128 | }
129 |
130 | .status-style {
131 | margin-bottom: -5px;
132 | }
133 |
134 | .update-button {
135 | display: flex;
136 | align-self: flex-end;
137 | }
138 |
139 | .modal-header {
140 | color: rgb(0, 0, 0);
141 | }
142 |
143 | .modal-body {
144 | color: rgb(0, 0, 0);
145 | font-size: 18px;
146 | display: flex;
147 | flex-direction: column;
148 | padding: 20px;
149 | }
150 |
151 | .date-input, .dropdown {
152 | width: 220px;
153 | }
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import './App.css';
3 | import ListJobs from './components/ListJobs';
4 | import NavBar from './components/NavBar';
5 | import AddJob from './components/AddJob';
6 |
7 |
8 | function App() {
9 |
10 | const [jobsList, setJobsList] = useState([]);
11 |
12 | const refreshJobList = async () => {
13 | try {
14 | console.log('Job List is refreshing')
15 | const res = await fetch("http://localhost:5000/job");
16 | const jsonData = await res.json();
17 | setJobsList(jsonData);
18 | } catch (error) {
19 | console.error(error);
20 | }
21 | };
22 | useEffect(() => {refreshJobList()} ,[])
23 | useEffect(() => {} ,[jobsList])
24 |
25 |
26 | return (
27 |
32 | )
33 | }
34 |
35 | export default App;
36 |
--------------------------------------------------------------------------------
/src/components/AddJob.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import PropTypes from "prop-types";
3 | import validator from "validator";
4 |
5 | const AddJob = ({ setJobsList, refreshJobList }) => {
6 |
7 | const [newJob, setNewJob] = useState({
8 | company_name: "",
9 | job_role: "",
10 | job_link: "",
11 | job_salary: "",
12 | date_applied: "",
13 | app_status: "",
14 | });
15 |
16 | const [errorMessages, setErrorMessages] = useState({
17 | company_name: "",
18 | job_role: ""
19 | });
20 |
21 | const [isDisabled, setIsDisabled] = useState(true);
22 |
23 | const changeHandler = (e) => {
24 | const { name, value } = e.target;
25 |
26 | if (name === "company_name") {
27 | if (!validator.isLength(value, { min: 0, max: 50 })) {
28 | setErrorMessages({
29 | ...errorMessages,
30 | company_name: "Max characters allowed reached",
31 | });
32 | } else {
33 | setErrorMessages({
34 | ...errorMessages,
35 | company_name: "",
36 | });
37 | }
38 | } else if (name === "job_role") {
39 | if (!validator.isLength(value, { min: 0, max: 50 })) {
40 | setErrorMessages({
41 | ...errorMessages,
42 | job_role: "Max characters allowed reached",
43 | });
44 | } else {
45 | setErrorMessages({
46 | ...errorMessages,
47 | job_role: "",
48 | });
49 | }
50 | }
51 |
52 | setNewJob({
53 | ...newJob,
54 | [name]: value
55 | });
56 | };
57 |
58 | useEffect(() => {
59 | if (validator.isLength(newJob.company_name, { min: 1, max: 50 }) && validator.isLength(newJob.job_role, { min: 1, max: 50 })) {
60 | setIsDisabled(false);
61 | } else {
62 | setIsDisabled(true);
63 | }
64 | }, [newJob]);
65 |
66 | const handleSubmitClick = async e => {
67 | e.preventDefault();
68 |
69 | try {
70 | const body = {
71 | company_name: newJob.company_name,
72 | job_role: newJob.job_role,
73 | job_link: newJob.job_link,
74 | job_salary: newJob.job_salary,
75 | date_applied: newJob.date_applied,
76 | app_status: newJob.app_status,
77 | };
78 | const response = await fetch("http://localhost:5000/job", {
79 | method: 'POST',
80 | headers: { 'Content-Type': 'application/json' },
81 | body: JSON.stringify(body)
82 | });
83 |
84 | const data = await response.json();
85 | setJobsList(prevJobsList => [...prevJobsList, data]);
86 | refreshJobList();
87 | setNewJob({
88 | company_name: "",
89 | job_role: "",
90 | job_link: "",
91 | job_salary: "",
92 | date_applied: "",
93 | app_status: "",
94 | });
95 | } catch (error) {
96 | console.error(error);
97 | }
98 | };
99 |
100 | return (
101 |
102 |
103 |
104 |
105 |
106 |
107 |
Add Job
108 |
113 |
114 |
115 |
116 |
193 |
194 |
195 |
202 | Submit
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 | )
211 | };
212 |
213 | AddJob.propTypes = {
214 | setJobsList: PropTypes.func.isRequired,
215 | };
216 |
217 | export default AddJob;
--------------------------------------------------------------------------------
/src/components/Helper.js:
--------------------------------------------------------------------------------
1 | // Helper function
2 |
3 | export function decodeHTMLEntities(text) {
4 | const element = document.createElement("div");
5 | element.innerHTML = text;
6 | return element.textContent || element.innerText;
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/src/components/ListJobs.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState, useRef } from "react";
2 | import UpdateJobs from "./UpdateJobs";
3 | import StatusCounts from "./StatusCounts";
4 | import { Typeahead } from 'react-bootstrap-typeahead';
5 | import 'react-bootstrap-typeahead/css/Typeahead.css';
6 | import { decodeHTMLEntities } from "./Helper";
7 |
8 | const ListJobs = ({ refreshJobList, jobsList }) => {
9 | const [selectedCompany, setSelectedCompany] = useState('');
10 | const [filteredJobsList, setFilteredJobsList] = useState([]);
11 | const [isSearchClicked, setIsSearchClicked] = useState(false);
12 |
13 | const sortJobs = jobsList
14 | .sort((a, b) => b.date_applied.localeCompare(a.date_applied))
15 | .reduce((acc, job) => {
16 | const date = job.date_applied;
17 | acc[date] = acc[date] || [];
18 | acc[date].push(job);
19 | return acc;
20 | }, {});
21 |
22 | const sortedJobsList = Object.values(sortJobs).flatMap((jobsDate) =>
23 | jobsDate.sort((a, b) => b.id - a.id)
24 | );
25 |
26 | const handleFilter = () => {
27 | const filteredList = jobsList.filter(
28 | (job) => job.company_name === selectedCompany
29 | );
30 | setFilteredJobsList(filteredList);
31 | setIsSearchClicked(true);
32 | };
33 |
34 | useEffect(() => {
35 | const filteredList = jobsList.filter(
36 | (job) => job.company_name === selectedCompany
37 | );
38 | setFilteredJobsList(filteredList);
39 | }, [jobsList, selectedCompany]);
40 |
41 | const handleClear = () => {
42 | setSelectedCompany("");
43 | setIsSearchClicked(false);
44 | setFilteredJobsList([]);
45 | typeaheadRef.current.clear();
46 | };
47 |
48 | const jobsToRender = isSearchClicked ? filteredJobsList : sortedJobsList;
49 |
50 | const typeaheadRef = useRef();
51 |
52 | return (
53 |
54 |
55 |
Applications: {jobsList.length}
56 |
59 |
60 |
67 | setSelectedCompany(selected[0]?.company_name || "")
68 | }
69 | ref={typeaheadRef}
70 | />
71 |
76 |
77 |
78 | {
79 | isSearchClicked
80 | &&
81 | (Clear )
85 | }
86 |
87 |
88 |
89 | {jobsToRender.map((job) => (
90 |
91 |
97 | {decodeHTMLEntities(job.company_name)}
98 |
99 |
{job.job_role}
100 |
{job.job_salary}
101 |
102 | Applied on:
103 |
104 | {new Date(job.date_applied).toLocaleDateString()}
105 |
106 |
107 | Status:
108 |
109 | {job.app_status}
110 |
111 |
112 |
118 |
119 |
120 |
121 |
126 |
127 | ))}
128 |
129 |
130 | );
131 | };
132 |
133 | export default ListJobs;
134 |
--------------------------------------------------------------------------------
/src/components/NavBar.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | function NavBar() {
4 | return (
5 |
6 |
Job Tracker
7 |
8 |
14 | Add Job
15 |
16 |
17 |
18 | )
19 | }
20 |
21 | export default NavBar;
--------------------------------------------------------------------------------
/src/components/StatusCounts.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const StatusCounts = ({ jobsList }) => {
4 |
5 | const counts = (jobsList || []).reduce(
6 | (acc, job) => {
7 |
8 | if (job.status_rejected && Date.parse(job.status_rejected)) {
9 | acc.rejectedCount++;
10 | }
11 | if (job.status_interviewed && Date.parse(job.status_interviewed)) {
12 | acc.interviewedCount++;
13 | }
14 | if (job.status_technical && Date.parse(job.status_technical)) {
15 | acc.technicalCount++;
16 | }
17 | if (job.status_offer && Date.parse(job.status_offer)) {
18 | acc.offerCount++;
19 | }
20 | return acc;
21 | },
22 | {
23 | rejectedCount: 0,
24 | interviewedCount: 0,
25 | technicalCount: 0,
26 | offerCount: 0,
27 | }
28 | );
29 |
30 | // console.log('status counts:', counts);
31 |
32 | return (
33 |
34 |
Rejected: {counts.rejectedCount}
35 | Interviewed: {counts.interviewedCount}
36 | Technical: {counts.technicalCount}
37 | Offer: {counts.offerCount}
38 |
39 | );
40 | };
41 |
42 | export default StatusCounts;
43 |
--------------------------------------------------------------------------------
/src/components/UpdateJobs.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import validator from "validator";
3 | import { decodeHTMLEntities } from "./Helper";
4 |
5 | const UpdateJobs = ({ job, refreshJobList }) => {
6 |
7 | const [jobInfo, setJobInfo] = useState({
8 | company_name: job.company_name,
9 | job_role: job.job_role,
10 | date_applied: job.date_applied,
11 | app_status: job.app_status,
12 | statusDate: "",
13 | status_rejected: job.status_rejected,
14 | status_interviewed: job.status_interviewed,
15 | status_technical: job.status_technical,
16 | status_offer: job.status_offer,
17 | job_link: job.job_link,
18 | job_salary: job.job_salary
19 | });
20 |
21 | const [errorMessages, setErrorMessages] = useState({
22 | company_name: "",
23 | job_role: ""
24 | });
25 |
26 | const [isDisabled, setIsDisabled] = useState(true);
27 |
28 | const changeHandler = (e) => {
29 | const { name, value } = e.target;
30 |
31 | if (name === "company_name") {
32 | if (!validator.isLength(value, { min: 0, max: 50 })) {
33 | setErrorMessages({
34 | ...errorMessages,
35 | company_name: "Max characters allowed reached",
36 | });
37 | } else {
38 | setErrorMessages({
39 | ...errorMessages,
40 | company_name: "",
41 | });
42 | }
43 | } else if (name === "job_role") {
44 | if (!validator.isLength(value, { min: 0, max: 50 })) {
45 | setErrorMessages({
46 | ...errorMessages,
47 | job_role: "Max characters allowed reached",
48 | });
49 | } else {
50 | setErrorMessages({
51 | ...errorMessages,
52 | job_role: "",
53 | });
54 | }
55 | };
56 |
57 | if (name === "app_status" && value !== "Applied") {
58 | setJobInfo((prevJobInfo) => {
59 | const statusToUpdate = value === "Rejected" ? "status_rejected" :
60 | value === "Interviewed" ? "status_interviewed" :
61 | value === "Technical" ? "status_technical" :
62 | value === "Offer" ? "status_offer" : null;
63 |
64 | if (statusToUpdate) {
65 | return {
66 | ...prevJobInfo,
67 | app_status: value,
68 | [statusToUpdate]: new Date().toISOString()
69 | };
70 | } else {
71 | return {
72 | ...prevJobInfo,
73 | app_status: value
74 | };
75 | }
76 | });
77 | } else if (name === "statusDate") {
78 | setJobInfo((prevJobInfo) => ({
79 | ...prevJobInfo,
80 | statusDate: value.toString(),
81 | status_rejected:
82 | prevJobInfo.app_status === "Rejected" ? value.toString() : prevJobInfo.status_rejected,
83 | status_interviewed:
84 | prevJobInfo.app_status === "Interviewed" ? value.toString() : prevJobInfo.status_interviewed,
85 | status_technical:
86 | prevJobInfo.app_status === "Technical" ? value.toString() : prevJobInfo.status_technical,
87 | status_offer:
88 | prevJobInfo.app_status === "Offer" ? value.toString() : prevJobInfo.status_offer
89 | }));
90 | } else {
91 | setJobInfo({ ...jobInfo, [name]: value });
92 | }
93 | };
94 |
95 | useEffect(() => {
96 | if (validator.isLength(jobInfo.company_name, { min: 1, max: 50 }) && validator.isLength(jobInfo.job_role, { min: 1, max: 50 })) {
97 | setIsDisabled(false);
98 | } else {
99 | setIsDisabled(true);
100 | }
101 | }, [jobInfo]);
102 |
103 | const updateJobInfo = async e => {
104 | e.preventDefault();
105 |
106 | try {
107 | const body = {
108 | company_name: jobInfo.company_name,
109 | job_role: jobInfo.job_role,
110 | date_applied: jobInfo.date_applied,
111 | app_status: jobInfo.app_status,
112 | statusDate: jobInfo.statusDate,
113 | status_rejected: jobInfo.app_status === "Rejected" ? jobInfo.statusDate : jobInfo.status_rejected,
114 | status_interviewed: jobInfo.app_status === "Interviewed" ? jobInfo.statusDate : jobInfo.status_interviewed,
115 | status_technical: jobInfo.app_status === "Technical" ? jobInfo.statusDate : jobInfo.status_technical,
116 | status_offer: jobInfo.app_status === "Offer" ? jobInfo.statusDate : jobInfo.status_offer,
117 | job_link: jobInfo.job_link,
118 | job_salary: jobInfo.job_salary,
119 | };
120 | console.log('updating job')
121 | await fetch(`http://localhost:5000/job/${job.id}`, {
122 | method: 'PUT',
123 | headers: { 'Content-Type': 'application/json'},
124 | body: JSON.stringify(body)
125 | });
126 | refreshJobList();
127 | } catch (error) {
128 | console.error(error);
129 | }
130 | }
131 |
132 | const deleteJob = async (id) => {
133 | try {
134 | await fetch(`http://localhost:5000/job/${job.id}`, {
135 | method: 'DELETE'
136 | });
137 | refreshJobList();
138 | } catch (error) {
139 | console.error(error)
140 | }
141 | }
142 |
143 | return (
144 |
145 |
146 |
147 |
148 |
149 |
150 |
Update Job
151 | setJobInfo(job)}
156 | >
157 |
158 |
159 |
160 |
251 |
252 |
253 | updateJobInfo(e)}
258 | disabled={isDisabled}
259 | >
260 | Update
261 |
262 |
263 | { window.confirm('Are you sure you want to delete this Job?',) && deleteJob(job.id) }}
268 | >
269 | Delete
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 | )
278 | }
279 |
280 | export default UpdateJobs;
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import App from './App';
4 | import "bootstrap-icons/font/bootstrap-icons.css";
5 |
6 | const root = ReactDOM.createRoot(document.getElementById('root'));
7 | root.render(
8 |
9 |
10 |
11 | );
12 |
13 |
--------------------------------------------------------------------------------