From 252775faf3fbbaaa7ddd645a3f7ac1f39e596d05 Mon Sep 17 00:00:00 2001 From: ReachableCEO Date: Thu, 16 Oct 2025 22:41:22 -0500 Subject: [PATCH] chore: sync infra docs and coverage --- .env.example | 5 +- .gitea/workflows/ci.yml | 12 +- backend/.eslintignore | 3 + backend/.eslintrc.js | 15 + backend/coverage/clover.xml | 1127 +++++++++++ backend/coverage/coverage-final.json | 13 + backend/coverage/lcov-report/base.css | 224 +++ .../coverage/lcov-report/block-navigation.js | 87 + backend/coverage/lcov-report/favicon.png | Bin 0 -> 445 bytes backend/coverage/lcov-report/index.html | 191 ++ backend/coverage/lcov-report/prettify.css | 1 + backend/coverage/lcov-report/prettify.js | 2 + .../lcov-report/sort-arrow-sprite.png | Bin 0 -> 138 bytes backend/coverage/lcov-report/sorter.js | 210 ++ .../lcov-report/src/config/index.html | 116 ++ .../lcov-report/src/config/index.js.html | 241 +++ .../src/database/connection.js.html | 154 ++ .../lcov-report/src/database/index.html | 116 ++ backend/coverage/lcov-report/src/index.html | 116 ++ .../lcov-report/src/middleware/auth.js.html | 250 +++ .../lcov-report/src/middleware/index.html | 116 ++ .../src/routes/applications.js.html | 1357 +++++++++++++ .../lcov-report/src/routes/auth.js.html | 547 +++++ .../lcov-report/src/routes/candidates.js.html | 1207 +++++++++++ .../lcov-report/src/routes/employers.js.html | 853 ++++++++ .../lcov-report/src/routes/index.html | 206 ++ .../lcov-report/src/routes/jobs.js.html | 1519 ++++++++++++++ .../lcov-report/src/routes/resumes.js.html | 988 +++++++++ .../lcov-report/src/routes/users.js.html | 580 ++++++ .../coverage/lcov-report/src/server.js.html | 271 +++ .../coverage/lcov-report/src/tests/index.html | 116 ++ .../lcov-report/src/tests/utils.js.html | 421 ++++ backend/coverage/lcov.info | 1768 +++++++++++++++++ backend/jest.config.js | 15 + backend/package-lock.json | 1372 ++++++++++++- backend/package.json | 11 +- backend/src/config/index.js | 29 +- backend/src/database/connection.js | 5 +- backend/src/database/migrate.js | 4 +- backend/src/database/seed.js | 16 +- backend/src/index.js | 2 +- backend/src/routes/resumes.js | 7 +- backend/src/server.js | 10 +- backend/src/tests/applications.test.js | 79 + backend/src/tests/auth.test.js | 35 +- backend/src/tests/candidates.test.js | 44 + backend/src/tests/employers.test.js | 34 + backend/src/tests/fixtures/resume.txt | 1 + backend/src/tests/globalSetup.js | 65 + backend/src/tests/globalTeardown.js | 20 + backend/src/tests/jobs.test.js | 44 +- backend/src/tests/resumes.test.js | 55 + backend/src/tests/setup.js | 17 + backend/src/tests/users.test.js | 35 + backend/src/tests/utils.js | 112 ++ deploy/coolify/docker-compose.yml | 4 + docker-compose.test.yml | 2 + docker-compose.yml | 4 + docs/COOLIFY_SANDBOX_CHECKLIST.md | 74 + frontend/.eslintignore | 3 + frontend/.eslintrc.js | 6 + frontend/coverage/clover.xml | 506 +++++ frontend/coverage/coverage-final.json | 19 + frontend/coverage/lcov-report/base.css | 224 +++ .../coverage/lcov-report/block-navigation.js | 87 + frontend/coverage/lcov-report/favicon.png | Bin 0 -> 445 bytes frontend/coverage/lcov-report/index.html | 176 ++ frontend/coverage/lcov-report/prettify.css | 1 + frontend/coverage/lcov-report/prettify.js | 2 + .../lcov-report/sort-arrow-sprite.png | Bin 0 -> 138 bytes frontend/coverage/lcov-report/sorter.js | 210 ++ frontend/coverage/lcov-report/src/App.js.html | 493 +++++ .../lcov-report/src/components/Layout.js.html | 643 ++++++ .../lcov-report/src/components/index.html | 116 ++ .../src/contexts/AuthContext.js.html | 400 ++++ .../lcov-report/src/contexts/index.html | 116 ++ frontend/coverage/lcov-report/src/index.html | 131 ++ .../coverage/lcov-report/src/index.js.html | 121 ++ .../src/lib/configureAxios.js.html | 118 ++ .../coverage/lcov-report/src/lib/index.html | 116 ++ .../src/pages/Applications.js.html | 418 ++++ .../src/pages/CandidateDetails.js.html | 532 +++++ .../lcov-report/src/pages/Candidates.js.html | 916 +++++++++ .../lcov-report/src/pages/CreateJob.js.html | 1471 ++++++++++++++ .../lcov-report/src/pages/Dashboard.js.html | 934 +++++++++ .../src/pages/EmployerDetails.js.html | 418 ++++ .../lcov-report/src/pages/Employers.js.html | 379 ++++ .../lcov-report/src/pages/JobDetails.js.html | 964 +++++++++ .../lcov-report/src/pages/Jobs.js.html | 1036 ++++++++++ .../lcov-report/src/pages/Login.js.html | 493 +++++ .../lcov-report/src/pages/Profile.js.html | 511 +++++ .../lcov-report/src/pages/Register.js.html | 811 ++++++++ .../lcov-report/src/pages/Resumes.js.html | 820 ++++++++ .../coverage/lcov-report/src/pages/index.html | 296 +++ frontend/coverage/lcov.info | 1194 +++++++++++ frontend/package-lock.json | 1 + frontend/package.json | 6 +- frontend/src/App.test.js | 6 +- frontend/src/components/Layout.test.js | 45 +- frontend/src/contexts/AuthContext.test.js | 87 + frontend/src/pages/Applications.js | 2 +- frontend/src/pages/CandidateDetails.js | 2 +- frontend/src/pages/CreateJob.js | 2 - frontend/src/pages/Dashboard.js | 13 +- frontend/src/pages/EmployerDetails.js | 2 +- frontend/src/pages/Employers.js | 2 +- frontend/src/pages/Jobs.js | 34 +- frontend/src/pages/Jobs.test.js | 68 + scripts/run-ci-tests.sh | 25 +- 109 files changed, 29696 insertions(+), 208 deletions(-) create mode 100644 backend/.eslintignore create mode 100644 backend/.eslintrc.js create mode 100644 backend/coverage/clover.xml create mode 100644 backend/coverage/coverage-final.json create mode 100644 backend/coverage/lcov-report/base.css create mode 100644 backend/coverage/lcov-report/block-navigation.js create mode 100644 backend/coverage/lcov-report/favicon.png create mode 100644 backend/coverage/lcov-report/index.html create mode 100644 backend/coverage/lcov-report/prettify.css create mode 100644 backend/coverage/lcov-report/prettify.js create mode 100644 backend/coverage/lcov-report/sort-arrow-sprite.png create mode 100644 backend/coverage/lcov-report/sorter.js create mode 100644 backend/coverage/lcov-report/src/config/index.html create mode 100644 backend/coverage/lcov-report/src/config/index.js.html create mode 100644 backend/coverage/lcov-report/src/database/connection.js.html create mode 100644 backend/coverage/lcov-report/src/database/index.html create mode 100644 backend/coverage/lcov-report/src/index.html create mode 100644 backend/coverage/lcov-report/src/middleware/auth.js.html create mode 100644 backend/coverage/lcov-report/src/middleware/index.html create mode 100644 backend/coverage/lcov-report/src/routes/applications.js.html create mode 100644 backend/coverage/lcov-report/src/routes/auth.js.html create mode 100644 backend/coverage/lcov-report/src/routes/candidates.js.html create mode 100644 backend/coverage/lcov-report/src/routes/employers.js.html create mode 100644 backend/coverage/lcov-report/src/routes/index.html create mode 100644 backend/coverage/lcov-report/src/routes/jobs.js.html create mode 100644 backend/coverage/lcov-report/src/routes/resumes.js.html create mode 100644 backend/coverage/lcov-report/src/routes/users.js.html create mode 100644 backend/coverage/lcov-report/src/server.js.html create mode 100644 backend/coverage/lcov-report/src/tests/index.html create mode 100644 backend/coverage/lcov-report/src/tests/utils.js.html create mode 100644 backend/coverage/lcov.info create mode 100644 backend/jest.config.js create mode 100644 backend/src/tests/applications.test.js create mode 100644 backend/src/tests/candidates.test.js create mode 100644 backend/src/tests/employers.test.js create mode 100644 backend/src/tests/fixtures/resume.txt create mode 100644 backend/src/tests/globalSetup.js create mode 100644 backend/src/tests/globalTeardown.js create mode 100644 backend/src/tests/resumes.test.js create mode 100644 backend/src/tests/setup.js create mode 100644 backend/src/tests/users.test.js create mode 100644 backend/src/tests/utils.js create mode 100644 docs/COOLIFY_SANDBOX_CHECKLIST.md create mode 100644 frontend/.eslintignore create mode 100644 frontend/.eslintrc.js create mode 100644 frontend/coverage/clover.xml create mode 100644 frontend/coverage/coverage-final.json create mode 100644 frontend/coverage/lcov-report/base.css create mode 100644 frontend/coverage/lcov-report/block-navigation.js create mode 100644 frontend/coverage/lcov-report/favicon.png create mode 100644 frontend/coverage/lcov-report/index.html create mode 100644 frontend/coverage/lcov-report/prettify.css create mode 100644 frontend/coverage/lcov-report/prettify.js create mode 100644 frontend/coverage/lcov-report/sort-arrow-sprite.png create mode 100644 frontend/coverage/lcov-report/sorter.js create mode 100644 frontend/coverage/lcov-report/src/App.js.html create mode 100644 frontend/coverage/lcov-report/src/components/Layout.js.html create mode 100644 frontend/coverage/lcov-report/src/components/index.html create mode 100644 frontend/coverage/lcov-report/src/contexts/AuthContext.js.html create mode 100644 frontend/coverage/lcov-report/src/contexts/index.html create mode 100644 frontend/coverage/lcov-report/src/index.html create mode 100644 frontend/coverage/lcov-report/src/index.js.html create mode 100644 frontend/coverage/lcov-report/src/lib/configureAxios.js.html create mode 100644 frontend/coverage/lcov-report/src/lib/index.html create mode 100644 frontend/coverage/lcov-report/src/pages/Applications.js.html create mode 100644 frontend/coverage/lcov-report/src/pages/CandidateDetails.js.html create mode 100644 frontend/coverage/lcov-report/src/pages/Candidates.js.html create mode 100644 frontend/coverage/lcov-report/src/pages/CreateJob.js.html create mode 100644 frontend/coverage/lcov-report/src/pages/Dashboard.js.html create mode 100644 frontend/coverage/lcov-report/src/pages/EmployerDetails.js.html create mode 100644 frontend/coverage/lcov-report/src/pages/Employers.js.html create mode 100644 frontend/coverage/lcov-report/src/pages/JobDetails.js.html create mode 100644 frontend/coverage/lcov-report/src/pages/Jobs.js.html create mode 100644 frontend/coverage/lcov-report/src/pages/Login.js.html create mode 100644 frontend/coverage/lcov-report/src/pages/Profile.js.html create mode 100644 frontend/coverage/lcov-report/src/pages/Register.js.html create mode 100644 frontend/coverage/lcov-report/src/pages/Resumes.js.html create mode 100644 frontend/coverage/lcov-report/src/pages/index.html create mode 100644 frontend/coverage/lcov.info create mode 100644 frontend/src/contexts/AuthContext.test.js create mode 100644 frontend/src/pages/Jobs.test.js diff --git a/.env.example b/.env.example index 4a4b79d..0d3a510 100644 --- a/.env.example +++ b/.env.example @@ -8,4 +8,7 @@ POSTGRES_USER=merchantsofhope_user POSTGRES_PASSWORD=merchantsofhope_password # Backend Application -JWT_SECRET=a_much_stronger_and_longer_secret_key_for_jwt_that_is_not_in_git \ No newline at end of file +JWT_SECRET=a_much_stronger_and_longer_secret_key_for_jwt_that_is_not_in_git +RATE_LIMIT_WINDOW_MS=900000 +RATE_LIMIT_MAX=100 +UPLOAD_DIR=uploads/resumes diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index dbfca5f..f20fa27 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -41,12 +41,16 @@ jobs: run: npm ci working-directory: backend + - name: Lint backend + run: npm run lint + working-directory: backend + - name: Run database migrations run: npm run migrate working-directory: backend - name: Run backend tests - run: npm test -- --runInBand + run: npm test -- --runInBand --coverage working-directory: backend frontend: @@ -68,8 +72,12 @@ jobs: run: npm ci working-directory: frontend + - name: Lint frontend + run: npm run lint + working-directory: frontend + - name: Run frontend tests - run: npm test -- --watchAll=false + run: npm test -- --watchAll=false --coverage working-directory: frontend docker-images: diff --git a/backend/.eslintignore b/backend/.eslintignore new file mode 100644 index 0000000..513966d --- /dev/null +++ b/backend/.eslintignore @@ -0,0 +1,3 @@ +node_modules +coverage +uploads diff --git a/backend/.eslintrc.js b/backend/.eslintrc.js new file mode 100644 index 0000000..bfaf7d5 --- /dev/null +++ b/backend/.eslintrc.js @@ -0,0 +1,15 @@ +module.exports = { + env: { + node: true, + es2021: true, + jest: true + }, + extends: ['eslint:recommended'], + parserOptions: { + ecmaVersion: 2021, + sourceType: 'script' + }, + rules: { + 'no-console': ['warn', { allow: ['error', 'info'] }] + } +}; diff --git a/backend/coverage/clover.xml b/backend/coverage/clover.xml new file mode 100644 index 0000000..bd90467 --- /dev/null +++ b/backend/coverage/clover.xml @@ -0,0 +1,1127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/coverage/coverage-final.json b/backend/coverage/coverage-final.json new file mode 100644 index 0000000..43b9b4e --- /dev/null +++ b/backend/coverage/coverage-final.json @@ -0,0 +1,13 @@ +{"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/server.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/server.js","statementMap":{"0":{"start":{"line":1,"column":16},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":13},"end":{"line":2,"column":28}},"2":{"start":{"line":3,"column":15},"end":{"line":3,"column":32}},"3":{"start":{"line":4,"column":15},"end":{"line":4,"column":32}},"4":{"start":{"line":5,"column":18},"end":{"line":5,"column":47}},"5":{"start":{"line":6,"column":15},"end":{"line":6,"column":34}},"6":{"start":{"line":8,"column":19},"end":{"line":8,"column":43}},"7":{"start":{"line":9,"column":19},"end":{"line":9,"column":44}},"8":{"start":{"line":10,"column":23},"end":{"line":10,"column":52}},"9":{"start":{"line":11,"column":24},"end":{"line":11,"column":54}},"10":{"start":{"line":12,"column":18},"end":{"line":12,"column":42}},"11":{"start":{"line":13,"column":26},"end":{"line":13,"column":58}},"12":{"start":{"line":14,"column":21},"end":{"line":14,"column":48}},"13":{"start":{"line":16,"column":12},"end":{"line":16,"column":21}},"14":{"start":{"line":18,"column":20},"end":{"line":23,"column":2}},"15":{"start":{"line":25,"column":0},"end":{"line":25,"column":28}},"16":{"start":{"line":27,"column":20},"end":{"line":29,"column":63}},"17":{"start":{"line":29,"column":49},"end":{"line":29,"column":62}},"18":{"start":{"line":31,"column":0},"end":{"line":31,"column":18}},"19":{"start":{"line":32,"column":0},"end":{"line":32,"column":77}},"20":{"start":{"line":33,"column":0},"end":{"line":33,"column":66}},"21":{"start":{"line":34,"column":0},"end":{"line":34,"column":41}},"22":{"start":{"line":35,"column":0},"end":{"line":35,"column":48}},"23":{"start":{"line":37,"column":0},"end":{"line":37,"column":46}},"24":{"start":{"line":38,"column":0},"end":{"line":38,"column":34}},"25":{"start":{"line":39,"column":0},"end":{"line":39,"column":42}},"26":{"start":{"line":40,"column":0},"end":{"line":40,"column":44}},"27":{"start":{"line":41,"column":0},"end":{"line":41,"column":32}},"28":{"start":{"line":42,"column":0},"end":{"line":42,"column":48}},"29":{"start":{"line":43,"column":0},"end":{"line":43,"column":38}},"30":{"start":{"line":45,"column":0},"end":{"line":47,"column":3}},"31":{"start":{"line":46,"column":2},"end":{"line":46,"column":66}},"32":{"start":{"line":50,"column":0},"end":{"line":56,"column":3}},"33":{"start":{"line":51,"column":2},"end":{"line":51,"column":27}},"34":{"start":{"line":52,"column":2},"end":{"line":55,"column":5}},"35":{"start":{"line":58,"column":0},"end":{"line":60,"column":3}},"36":{"start":{"line":59,"column":2},"end":{"line":59,"column":53}},"37":{"start":{"line":62,"column":0},"end":{"line":62,"column":21}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":29,"column":37},"end":{"line":29,"column":38}},"loc":{"start":{"line":29,"column":49},"end":{"line":29,"column":62}},"line":29},"1":{"name":"(anonymous_1)","decl":{"start":{"line":45,"column":23},"end":{"line":45,"column":24}},"loc":{"start":{"line":45,"column":37},"end":{"line":47,"column":1}},"line":45},"2":{"name":"(anonymous_2)","decl":{"start":{"line":50,"column":8},"end":{"line":50,"column":9}},"loc":{"start":{"line":50,"column":33},"end":{"line":56,"column":1}},"line":50},"3":{"name":"(anonymous_3)","decl":{"start":{"line":58,"column":13},"end":{"line":58,"column":14}},"loc":{"start":{"line":58,"column":27},"end":{"line":60,"column":1}},"line":58}},"branchMap":{"0":{"loc":{"start":{"line":27,"column":20},"end":{"line":29,"column":63}},"type":"cond-expr","locations":[{"start":{"line":28,"column":4},"end":{"line":28,"column":13}},{"start":{"line":29,"column":4},"end":{"line":29,"column":63}}],"line":27},"1":{"loc":{"start":{"line":32,"column":13},"end":{"line":32,"column":74}},"type":"cond-expr","locations":[{"start":{"line":32,"column":27},"end":{"line":32,"column":69}},{"start":{"line":32,"column":72},"end":{"line":32,"column":74}}],"line":32},"2":{"loc":{"start":{"line":33,"column":15},"end":{"line":33,"column":63}},"type":"cond-expr","locations":[{"start":{"line":33,"column":45},"end":{"line":33,"column":55}},{"start":{"line":33,"column":58},"end":{"line":33,"column":63}}],"line":33},"3":{"loc":{"start":{"line":54,"column":13},"end":{"line":54,"column":81}},"type":"cond-expr","locations":[{"start":{"line":54,"column":44},"end":{"line":54,"column":55}},{"start":{"line":54,"column":58},"end":{"line":54,"column":81}}],"line":54}},"s":{"0":7,"1":7,"2":7,"3":7,"4":7,"5":7,"6":7,"7":7,"8":7,"9":7,"10":7,"11":7,"12":7,"13":7,"14":7,"15":7,"16":7,"17":0,"18":7,"19":7,"20":7,"21":7,"22":7,"23":7,"24":7,"25":7,"26":7,"27":7,"28":7,"29":7,"30":7,"31":0,"32":7,"33":0,"34":0,"35":7,"36":0,"37":7},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[7,0],"1":[0,7],"2":[0,7],"3":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"a776deb60ae27a82c4090f50aab58cb3514a71d6"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/config/index.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/config/index.js","statementMap":{"0":{"start":{"line":1,"column":15},"end":{"line":1,"column":32}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":16}},"2":{"start":{"line":5,"column":23},"end":{"line":8,"column":1}},"3":{"start":{"line":6,"column":17},"end":{"line":6,"column":36}},"4":{"start":{"line":7,"column":2},"end":{"line":7,"column":53}},"5":{"start":{"line":10,"column":26},"end":{"line":28,"column":1}},"6":{"start":{"line":11,"column":2},"end":{"line":13,"column":3}},"7":{"start":{"line":12,"column":4},"end":{"line":12,"column":36}},"8":{"start":{"line":21,"column":6},"end":{"line":21,"column":17}},"9":{"start":{"line":23,"column":2},"end":{"line":25,"column":3}},"10":{"start":{"line":24,"column":4},"end":{"line":24,"column":21}},"11":{"start":{"line":27,"column":2},"end":{"line":27,"column":111}},"12":{"start":{"line":30,"column":15},"end":{"line":43,"column":1}},"13":{"start":{"line":45,"column":17},"end":{"line":45,"column":45}},"14":{"start":{"line":46,"column":20},"end":{"line":46,"column":58}},"15":{"start":{"line":46,"column":45},"end":{"line":46,"column":57}},"16":{"start":{"line":48,"column":0},"end":{"line":50,"column":1}},"17":{"start":{"line":49,"column":2},"end":{"line":49,"column":87}},"18":{"start":{"line":52,"column":0},"end":{"line":52,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":23},"end":{"line":5,"column":24}},"loc":{"start":{"line":5,"column":44},"end":{"line":8,"column":1}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":10,"column":26},"end":{"line":10,"column":27}},"loc":{"start":{"line":10,"column":32},"end":{"line":28,"column":1}},"line":10},"2":{"name":"(anonymous_2)","decl":{"start":{"line":46,"column":36},"end":{"line":46,"column":37}},"loc":{"start":{"line":46,"column":45},"end":{"line":46,"column":57}},"line":46}},"branchMap":{"0":{"loc":{"start":{"line":7,"column":9},"end":{"line":7,"column":52}},"type":"cond-expr","locations":[{"start":{"line":7,"column":35},"end":{"line":7,"column":41}},{"start":{"line":7,"column":44},"end":{"line":7,"column":52}}],"line":7},"1":{"loc":{"start":{"line":11,"column":2},"end":{"line":13,"column":3}},"type":"if","locations":[{"start":{"line":11,"column":2},"end":{"line":13,"column":3}},{"start":{},"end":{}}],"line":11},"2":{"loc":{"start":{"line":16,"column":4},"end":{"line":16,"column":42}},"type":"default-arg","locations":[{"start":{"line":16,"column":20},"end":{"line":16,"column":42}}],"line":16},"3":{"loc":{"start":{"line":18,"column":4},"end":{"line":18,"column":57}},"type":"default-arg","locations":[{"start":{"line":18,"column":18},"end":{"line":18,"column":57}}],"line":18},"4":{"loc":{"start":{"line":19,"column":4},"end":{"line":19,"column":68}},"type":"default-arg","locations":[{"start":{"line":19,"column":20},"end":{"line":19,"column":68}}],"line":19},"5":{"loc":{"start":{"line":20,"column":4},"end":{"line":20,"column":26}},"type":"default-arg","locations":[{"start":{"line":20,"column":20},"end":{"line":20,"column":26}}],"line":20},"6":{"loc":{"start":{"line":23,"column":2},"end":{"line":25,"column":3}},"type":"if","locations":[{"start":{"line":23,"column":2},"end":{"line":25,"column":3}},{"start":{},"end":{}}],"line":23},"7":{"loc":{"start":{"line":31,"column":7},"end":{"line":31,"column":44}},"type":"binary-expr","locations":[{"start":{"line":31,"column":7},"end":{"line":31,"column":27}},{"start":{"line":31,"column":31},"end":{"line":31,"column":44}}],"line":31},"8":{"loc":{"start":{"line":32,"column":8},"end":{"line":32,"column":65}},"type":"binary-expr","locations":[{"start":{"line":32,"column":8},"end":{"line":32,"column":24}},{"start":{"line":32,"column":28},"end":{"line":32,"column":52}},{"start":{"line":32,"column":56},"end":{"line":32,"column":65}}],"line":32},"9":{"loc":{"start":{"line":33,"column":23},"end":{"line":33,"column":67}},"type":"binary-expr","locations":[{"start":{"line":33,"column":23},"end":{"line":33,"column":39}},{"start":{"line":33,"column":43},"end":{"line":33,"column":67}}],"line":33},"10":{"loc":{"start":{"line":36,"column":14},"end":{"line":36,"column":44}},"type":"binary-expr","locations":[{"start":{"line":36,"column":14},"end":{"line":36,"column":37}},{"start":{"line":36,"column":41},"end":{"line":36,"column":44}}],"line":36},"11":{"loc":{"start":{"line":37,"column":12},"end":{"line":37,"column":43}},"type":"binary-expr","locations":[{"start":{"line":37,"column":12},"end":{"line":37,"column":33}},{"start":{"line":37,"column":37},"end":{"line":37,"column":43}}],"line":37},"12":{"loc":{"start":{"line":38,"column":13},"end":{"line":38,"column":56}},"type":"binary-expr","locations":[{"start":{"line":38,"column":13},"end":{"line":38,"column":35}},{"start":{"line":38,"column":39},"end":{"line":38,"column":56}}],"line":38},"13":{"loc":{"start":{"line":48,"column":0},"end":{"line":50,"column":1}},"type":"if","locations":[{"start":{"line":48,"column":0},"end":{"line":50,"column":1}},{"start":{},"end":{}}],"line":48}},"s":{"0":7,"1":7,"2":7,"3":21,"4":21,"5":7,"6":7,"7":0,"8":7,"9":7,"10":0,"11":7,"12":7,"13":7,"14":7,"15":14,"16":7,"17":0,"18":7},"f":{"0":21,"1":7,"2":14},"b":{"0":[0,21],"1":[0,7],"2":[0],"3":[0],"4":[0],"5":[0],"6":[0,7],"7":[7,0],"8":[7,7,7],"9":[7,7],"10":[7,7],"11":[7,7],"12":[7,7],"13":[0,7]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"7030495d06c7ada2b8c4409e51819e7e8a9b5ec2"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/database/connection.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/database/connection.js","statementMap":{"0":{"start":{"line":1,"column":17},"end":{"line":1,"column":30}},"1":{"start":{"line":2,"column":15},"end":{"line":2,"column":35}},"2":{"start":{"line":4,"column":13},"end":{"line":7,"column":2}},"3":{"start":{"line":10,"column":0},"end":{"line":14,"column":3}},"4":{"start":{"line":11,"column":2},"end":{"line":13,"column":3}},"5":{"start":{"line":12,"column":4},"end":{"line":12,"column":80}},"6":{"start":{"line":16,"column":0},"end":{"line":21,"column":3}},"7":{"start":{"line":17,"column":2},"end":{"line":19,"column":3}},"8":{"start":{"line":18,"column":4},"end":{"line":18,"column":11}},"9":{"start":{"line":20,"column":2},"end":{"line":20,"column":51}},"10":{"start":{"line":23,"column":0},"end":{"line":23,"column":22}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":10,"column":19},"end":{"line":10,"column":20}},"loc":{"start":{"line":10,"column":25},"end":{"line":14,"column":1}},"line":10},"1":{"name":"(anonymous_1)","decl":{"start":{"line":16,"column":17},"end":{"line":16,"column":18}},"loc":{"start":{"line":16,"column":26},"end":{"line":21,"column":1}},"line":16}},"branchMap":{"0":{"loc":{"start":{"line":6,"column":7},"end":{"line":6,"column":74}},"type":"cond-expr","locations":[{"start":{"line":6,"column":37},"end":{"line":6,"column":66}},{"start":{"line":6,"column":69},"end":{"line":6,"column":74}}],"line":6},"1":{"loc":{"start":{"line":11,"column":2},"end":{"line":13,"column":3}},"type":"if","locations":[{"start":{"line":11,"column":2},"end":{"line":13,"column":3}},{"start":{},"end":{}}],"line":11},"2":{"loc":{"start":{"line":17,"column":2},"end":{"line":19,"column":3}},"type":"if","locations":[{"start":{"line":17,"column":2},"end":{"line":19,"column":3}},{"start":{},"end":{}}],"line":17},"3":{"loc":{"start":{"line":17,"column":6},"end":{"line":17,"column":77}},"type":"binary-expr","locations":[{"start":{"line":17,"column":6},"end":{"line":17,"column":26}},{"start":{"line":17,"column":30},"end":{"line":17,"column":77}}],"line":17}},"s":{"0":7,"1":7,"2":7,"3":7,"4":7,"5":7,"6":7,"7":0,"8":0,"9":0,"10":7},"f":{"0":7,"1":0},"b":{"0":[0,7],"1":[7,0],"2":[0,0],"3":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"a81f1f212f550bcda141cc0bb80f8186f83d36f3"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/middleware/auth.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/middleware/auth.js","statementMap":{"0":{"start":{"line":1,"column":12},"end":{"line":1,"column":35}},"1":{"start":{"line":2,"column":13},"end":{"line":2,"column":46}},"2":{"start":{"line":3,"column":15},"end":{"line":3,"column":35}},"3":{"start":{"line":5,"column":26},"end":{"line":36,"column":1}},"4":{"start":{"line":6,"column":21},"end":{"line":6,"column":49}},"5":{"start":{"line":7,"column":16},"end":{"line":7,"column":54}},"6":{"start":{"line":9,"column":2},"end":{"line":11,"column":3}},"7":{"start":{"line":10,"column":4},"end":{"line":10,"column":68}},"8":{"start":{"line":13,"column":2},"end":{"line":35,"column":3}},"9":{"start":{"line":14,"column":20},"end":{"line":14,"column":55}},"10":{"start":{"line":17,"column":23},"end":{"line":20,"column":5}},"11":{"start":{"line":22,"column":4},"end":{"line":24,"column":5}},"12":{"start":{"line":23,"column":6},"end":{"line":23,"column":62}},"13":{"start":{"line":26,"column":17},"end":{"line":26,"column":35}},"14":{"start":{"line":27,"column":4},"end":{"line":29,"column":5}},"15":{"start":{"line":28,"column":6},"end":{"line":28,"column":68}},"16":{"start":{"line":31,"column":4},"end":{"line":31,"column":20}},"17":{"start":{"line":32,"column":4},"end":{"line":32,"column":11}},"18":{"start":{"line":34,"column":4},"end":{"line":34,"column":71}},"19":{"start":{"line":38,"column":20},"end":{"line":50,"column":1}},"20":{"start":{"line":39,"column":2},"end":{"line":49,"column":4}},"21":{"start":{"line":40,"column":4},"end":{"line":42,"column":5}},"22":{"start":{"line":41,"column":6},"end":{"line":41,"column":72}},"23":{"start":{"line":44,"column":4},"end":{"line":46,"column":5}},"24":{"start":{"line":45,"column":6},"end":{"line":45,"column":73}},"25":{"start":{"line":48,"column":4},"end":{"line":48,"column":11}},"26":{"start":{"line":52,"column":0},"end":{"line":55,"column":2}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":26},"end":{"line":5,"column":27}},"loc":{"start":{"line":5,"column":52},"end":{"line":36,"column":1}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":38,"column":20},"end":{"line":38,"column":21}},"loc":{"start":{"line":38,"column":31},"end":{"line":50,"column":1}},"line":38},"2":{"name":"(anonymous_2)","decl":{"start":{"line":39,"column":9},"end":{"line":39,"column":10}},"loc":{"start":{"line":39,"column":29},"end":{"line":49,"column":3}},"line":39}},"branchMap":{"0":{"loc":{"start":{"line":7,"column":16},"end":{"line":7,"column":54}},"type":"binary-expr","locations":[{"start":{"line":7,"column":16},"end":{"line":7,"column":26}},{"start":{"line":7,"column":30},"end":{"line":7,"column":54}}],"line":7},"1":{"loc":{"start":{"line":9,"column":2},"end":{"line":11,"column":3}},"type":"if","locations":[{"start":{"line":9,"column":2},"end":{"line":11,"column":3}},{"start":{},"end":{}}],"line":9},"2":{"loc":{"start":{"line":22,"column":4},"end":{"line":24,"column":5}},"type":"if","locations":[{"start":{"line":22,"column":4},"end":{"line":24,"column":5}},{"start":{},"end":{}}],"line":22},"3":{"loc":{"start":{"line":27,"column":4},"end":{"line":29,"column":5}},"type":"if","locations":[{"start":{"line":27,"column":4},"end":{"line":29,"column":5}},{"start":{},"end":{}}],"line":27},"4":{"loc":{"start":{"line":40,"column":4},"end":{"line":42,"column":5}},"type":"if","locations":[{"start":{"line":40,"column":4},"end":{"line":42,"column":5}},{"start":{},"end":{}}],"line":40},"5":{"loc":{"start":{"line":44,"column":4},"end":{"line":46,"column":5}},"type":"if","locations":[{"start":{"line":44,"column":4},"end":{"line":46,"column":5}},{"start":{},"end":{}}],"line":44}},"s":{"0":7,"1":7,"2":7,"3":7,"4":53,"5":53,"6":53,"7":1,"8":52,"9":52,"10":51,"11":51,"12":0,"13":51,"14":51,"15":0,"16":51,"17":51,"18":1,"19":7,"20":70,"21":31,"22":0,"23":31,"24":0,"25":31,"26":7},"f":{"0":53,"1":70,"2":31},"b":{"0":[53,52],"1":[1,52],"2":[0,51],"3":[0,51],"4":[0,31],"5":[0,31]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"99dba35b8efe7460728fc2823d2ff725e5b06bcc"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/routes/applications.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/routes/applications.js","statementMap":{"0":{"start":{"line":1,"column":16},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":35},"end":{"line":2,"column":63}},"2":{"start":{"line":3,"column":13},"end":{"line":3,"column":46}},"3":{"start":{"line":4,"column":43},"end":{"line":4,"column":72}},"4":{"start":{"line":6,"column":15},"end":{"line":6,"column":31}},"5":{"start":{"line":9,"column":0},"end":{"line":183,"column":3}},"6":{"start":{"line":10,"column":2},"end":{"line":182,"column":3}},"7":{"start":{"line":18,"column":8},"end":{"line":18,"column":17}},"8":{"start":{"line":20,"column":16},"end":{"line":34,"column":5}},"9":{"start":{"line":35,"column":24},"end":{"line":35,"column":26}},"10":{"start":{"line":36,"column":21},"end":{"line":36,"column":22}},"11":{"start":{"line":37,"column":23},"end":{"line":37,"column":25}},"12":{"start":{"line":39,"column":4},"end":{"line":43,"column":5}},"13":{"start":{"line":40,"column":6},"end":{"line":40,"column":19}},"14":{"start":{"line":41,"column":6},"end":{"line":41,"column":51}},"15":{"start":{"line":42,"column":6},"end":{"line":42,"column":30}},"16":{"start":{"line":45,"column":4},"end":{"line":49,"column":5}},"17":{"start":{"line":46,"column":6},"end":{"line":46,"column":19}},"18":{"start":{"line":47,"column":6},"end":{"line":47,"column":57}},"19":{"start":{"line":48,"column":6},"end":{"line":48,"column":36}},"20":{"start":{"line":51,"column":4},"end":{"line":55,"column":5}},"21":{"start":{"line":52,"column":6},"end":{"line":52,"column":19}},"22":{"start":{"line":53,"column":6},"end":{"line":53,"column":51}},"23":{"start":{"line":54,"column":6},"end":{"line":54,"column":31}},"24":{"start":{"line":57,"column":4},"end":{"line":61,"column":5}},"25":{"start":{"line":58,"column":6},"end":{"line":58,"column":19}},"26":{"start":{"line":59,"column":6},"end":{"line":59,"column":56}},"27":{"start":{"line":60,"column":6},"end":{"line":60,"column":35}},"28":{"start":{"line":64,"column":4},"end":{"line":86,"column":5}},"29":{"start":{"line":66,"column":30},"end":{"line":69,"column":7}},"30":{"start":{"line":70,"column":6},"end":{"line":74,"column":7}},"31":{"start":{"line":71,"column":8},"end":{"line":71,"column":21}},"32":{"start":{"line":72,"column":8},"end":{"line":72,"column":59}},"33":{"start":{"line":73,"column":8},"end":{"line":73,"column":53}},"34":{"start":{"line":75,"column":11},"end":{"line":86,"column":5}},"35":{"start":{"line":77,"column":29},"end":{"line":80,"column":7}},"36":{"start":{"line":81,"column":6},"end":{"line":85,"column":7}},"37":{"start":{"line":82,"column":8},"end":{"line":82,"column":21}},"38":{"start":{"line":83,"column":8},"end":{"line":83,"column":58}},"39":{"start":{"line":84,"column":8},"end":{"line":84,"column":52}},"40":{"start":{"line":88,"column":4},"end":{"line":90,"column":5}},"41":{"start":{"line":89,"column":6},"end":{"line":89,"column":52}},"42":{"start":{"line":92,"column":4},"end":{"line":92,"column":43}},"43":{"start":{"line":95,"column":19},"end":{"line":95,"column":37}},"44":{"start":{"line":96,"column":4},"end":{"line":96,"column":17}},"45":{"start":{"line":97,"column":4},"end":{"line":97,"column":37}},"46":{"start":{"line":98,"column":4},"end":{"line":98,"column":28}},"47":{"start":{"line":99,"column":4},"end":{"line":99,"column":17}},"48":{"start":{"line":100,"column":4},"end":{"line":100,"column":38}},"49":{"start":{"line":101,"column":4},"end":{"line":101,"column":29}},"50":{"start":{"line":103,"column":19},"end":{"line":103,"column":55}},"51":{"start":{"line":106,"column":21},"end":{"line":112,"column":5}},"52":{"start":{"line":113,"column":24},"end":{"line":113,"column":26}},"53":{"start":{"line":114,"column":26},"end":{"line":114,"column":27}},"54":{"start":{"line":115,"column":28},"end":{"line":115,"column":30}},"55":{"start":{"line":117,"column":4},"end":{"line":121,"column":5}},"56":{"start":{"line":118,"column":6},"end":{"line":118,"column":24}},"57":{"start":{"line":119,"column":6},"end":{"line":119,"column":61}},"58":{"start":{"line":120,"column":6},"end":{"line":120,"column":30}},"59":{"start":{"line":123,"column":4},"end":{"line":127,"column":5}},"60":{"start":{"line":124,"column":6},"end":{"line":124,"column":24}},"61":{"start":{"line":125,"column":6},"end":{"line":125,"column":67}},"62":{"start":{"line":126,"column":6},"end":{"line":126,"column":36}},"63":{"start":{"line":129,"column":4},"end":{"line":133,"column":5}},"64":{"start":{"line":130,"column":6},"end":{"line":130,"column":24}},"65":{"start":{"line":131,"column":6},"end":{"line":131,"column":61}},"66":{"start":{"line":132,"column":6},"end":{"line":132,"column":31}},"67":{"start":{"line":135,"column":4},"end":{"line":139,"column":5}},"68":{"start":{"line":136,"column":6},"end":{"line":136,"column":24}},"69":{"start":{"line":137,"column":6},"end":{"line":137,"column":66}},"70":{"start":{"line":138,"column":6},"end":{"line":138,"column":35}},"71":{"start":{"line":142,"column":4},"end":{"line":162,"column":5}},"72":{"start":{"line":143,"column":30},"end":{"line":146,"column":7}},"73":{"start":{"line":147,"column":6},"end":{"line":151,"column":7}},"74":{"start":{"line":148,"column":8},"end":{"line":148,"column":26}},"75":{"start":{"line":149,"column":8},"end":{"line":149,"column":69}},"76":{"start":{"line":150,"column":8},"end":{"line":150,"column":53}},"77":{"start":{"line":152,"column":11},"end":{"line":162,"column":5}},"78":{"start":{"line":153,"column":29},"end":{"line":156,"column":7}},"79":{"start":{"line":157,"column":6},"end":{"line":161,"column":7}},"80":{"start":{"line":158,"column":8},"end":{"line":158,"column":26}},"81":{"start":{"line":159,"column":8},"end":{"line":159,"column":68}},"82":{"start":{"line":160,"column":8},"end":{"line":160,"column":52}},"83":{"start":{"line":164,"column":4},"end":{"line":166,"column":5}},"84":{"start":{"line":165,"column":6},"end":{"line":165,"column":62}},"85":{"start":{"line":168,"column":24},"end":{"line":168,"column":65}},"86":{"start":{"line":170,"column":4},"end":{"line":178,"column":7}},"87":{"start":{"line":180,"column":4},"end":{"line":180,"column":52}},"88":{"start":{"line":181,"column":4},"end":{"line":181,"column":68}},"89":{"start":{"line":186,"column":0},"end":{"line":238,"column":3}},"90":{"start":{"line":187,"column":2},"end":{"line":237,"column":3}},"91":{"start":{"line":188,"column":19},"end":{"line":188,"column":29}},"92":{"start":{"line":190,"column":19},"end":{"line":206,"column":12}},"93":{"start":{"line":208,"column":4},"end":{"line":210,"column":5}},"94":{"start":{"line":209,"column":6},"end":{"line":209,"column":70}},"95":{"start":{"line":212,"column":24},"end":{"line":212,"column":38}},"96":{"start":{"line":215,"column":4},"end":{"line":231,"column":5}},"97":{"start":{"line":216,"column":30},"end":{"line":219,"column":7}},"98":{"start":{"line":220,"column":6},"end":{"line":222,"column":7}},"99":{"start":{"line":221,"column":8},"end":{"line":221,"column":64}},"100":{"start":{"line":223,"column":11},"end":{"line":231,"column":5}},"101":{"start":{"line":224,"column":29},"end":{"line":227,"column":7}},"102":{"start":{"line":228,"column":6},"end":{"line":230,"column":7}},"103":{"start":{"line":229,"column":8},"end":{"line":229,"column":64}},"104":{"start":{"line":233,"column":4},"end":{"line":233,"column":26}},"105":{"start":{"line":235,"column":4},"end":{"line":235,"column":51}},"106":{"start":{"line":236,"column":4},"end":{"line":236,"column":67}},"107":{"start":{"line":241,"column":0},"end":{"line":304,"column":3}},"108":{"start":{"line":246,"column":2},"end":{"line":303,"column":3}},"109":{"start":{"line":247,"column":19},"end":{"line":247,"column":40}},"110":{"start":{"line":248,"column":4},"end":{"line":250,"column":5}},"111":{"start":{"line":249,"column":6},"end":{"line":249,"column":62}},"112":{"start":{"line":252,"column":42},"end":{"line":252,"column":50}},"113":{"start":{"line":255,"column":28},"end":{"line":258,"column":5}},"114":{"start":{"line":260,"column":4},"end":{"line":262,"column":5}},"115":{"start":{"line":261,"column":6},"end":{"line":261,"column":76}},"116":{"start":{"line":264,"column":24},"end":{"line":264,"column":50}},"117":{"start":{"line":267,"column":22},"end":{"line":270,"column":5}},"118":{"start":{"line":272,"column":4},"end":{"line":274,"column":5}},"119":{"start":{"line":273,"column":6},"end":{"line":273,"column":62}},"120":{"start":{"line":276,"column":4},"end":{"line":278,"column":5}},"121":{"start":{"line":277,"column":6},"end":{"line":277,"column":82}},"122":{"start":{"line":281,"column":32},"end":{"line":284,"column":5}},"123":{"start":{"line":286,"column":4},"end":{"line":288,"column":5}},"124":{"start":{"line":287,"column":6},"end":{"line":287,"column":75}},"125":{"start":{"line":290,"column":19},"end":{"line":294,"column":48}},"126":{"start":{"line":296,"column":4},"end":{"line":299,"column":7}},"127":{"start":{"line":301,"column":4},"end":{"line":301,"column":54}},"128":{"start":{"line":302,"column":4},"end":{"line":302,"column":68}},"129":{"start":{"line":307,"column":0},"end":{"line":368,"column":3}},"130":{"start":{"line":310,"column":2},"end":{"line":367,"column":3}},"131":{"start":{"line":311,"column":19},"end":{"line":311,"column":40}},"132":{"start":{"line":312,"column":4},"end":{"line":314,"column":5}},"133":{"start":{"line":313,"column":6},"end":{"line":313,"column":62}},"134":{"start":{"line":316,"column":19},"end":{"line":316,"column":29}},"135":{"start":{"line":317,"column":23},"end":{"line":317,"column":31}},"136":{"start":{"line":320,"column":30},"end":{"line":326,"column":12}},"137":{"start":{"line":328,"column":4},"end":{"line":330,"column":5}},"138":{"start":{"line":329,"column":6},"end":{"line":329,"column":70}},"139":{"start":{"line":332,"column":24},"end":{"line":332,"column":49}},"140":{"start":{"line":335,"column":4},"end":{"line":353,"column":5}},"141":{"start":{"line":336,"column":30},"end":{"line":339,"column":7}},"142":{"start":{"line":340,"column":6},"end":{"line":342,"column":7}},"143":{"start":{"line":341,"column":8},"end":{"line":341,"column":64}},"144":{"start":{"line":344,"column":6},"end":{"line":346,"column":7}},"145":{"start":{"line":345,"column":8},"end":{"line":345,"column":92}},"146":{"start":{"line":347,"column":11},"end":{"line":353,"column":5}},"147":{"start":{"line":348,"column":6},"end":{"line":350,"column":7}},"148":{"start":{"line":349,"column":8},"end":{"line":349,"column":64}},"149":{"start":{"line":351,"column":11},"end":{"line":353,"column":5}},"150":{"start":{"line":352,"column":6},"end":{"line":352,"column":62}},"151":{"start":{"line":355,"column":19},"end":{"line":358,"column":5}},"152":{"start":{"line":360,"column":4},"end":{"line":363,"column":7}},"153":{"start":{"line":365,"column":4},"end":{"line":365,"column":61}},"154":{"start":{"line":366,"column":4},"end":{"line":366,"column":75}},"155":{"start":{"line":371,"column":0},"end":{"line":422,"column":3}},"156":{"start":{"line":374,"column":2},"end":{"line":421,"column":3}},"157":{"start":{"line":375,"column":19},"end":{"line":375,"column":40}},"158":{"start":{"line":376,"column":4},"end":{"line":378,"column":5}},"159":{"start":{"line":377,"column":6},"end":{"line":377,"column":62}},"160":{"start":{"line":380,"column":19},"end":{"line":380,"column":29}},"161":{"start":{"line":381,"column":22},"end":{"line":381,"column":30}},"162":{"start":{"line":384,"column":30},"end":{"line":390,"column":12}},"163":{"start":{"line":392,"column":4},"end":{"line":394,"column":5}},"164":{"start":{"line":393,"column":6},"end":{"line":393,"column":70}},"165":{"start":{"line":396,"column":24},"end":{"line":396,"column":49}},"166":{"start":{"line":399,"column":4},"end":{"line":407,"column":5}},"167":{"start":{"line":400,"column":6},"end":{"line":400,"column":92}},"168":{"start":{"line":401,"column":11},"end":{"line":407,"column":5}},"169":{"start":{"line":402,"column":6},"end":{"line":404,"column":7}},"170":{"start":{"line":403,"column":8},"end":{"line":403,"column":64}},"171":{"start":{"line":405,"column":11},"end":{"line":407,"column":5}},"172":{"start":{"line":406,"column":6},"end":{"line":406,"column":62}},"173":{"start":{"line":409,"column":19},"end":{"line":412,"column":5}},"174":{"start":{"line":414,"column":4},"end":{"line":417,"column":7}},"175":{"start":{"line":419,"column":4},"end":{"line":419,"column":60}},"176":{"start":{"line":420,"column":4},"end":{"line":420,"column":74}},"177":{"start":{"line":424,"column":0},"end":{"line":424,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":9,"column":35},"end":{"line":9,"column":36}},"loc":{"start":{"line":9,"column":55},"end":{"line":183,"column":1}},"line":9},"1":{"name":"(anonymous_1)","decl":{"start":{"line":186,"column":38},"end":{"line":186,"column":39}},"loc":{"start":{"line":186,"column":58},"end":{"line":238,"column":1}},"line":186},"2":{"name":"(anonymous_2)","decl":{"start":{"line":245,"column":3},"end":{"line":245,"column":4}},"loc":{"start":{"line":245,"column":23},"end":{"line":304,"column":1}},"line":245},"3":{"name":"(anonymous_3)","decl":{"start":{"line":309,"column":3},"end":{"line":309,"column":4}},"loc":{"start":{"line":309,"column":23},"end":{"line":368,"column":1}},"line":309},"4":{"name":"(anonymous_4)","decl":{"start":{"line":373,"column":3},"end":{"line":373,"column":4}},"loc":{"start":{"line":373,"column":23},"end":{"line":422,"column":1}},"line":373}},"branchMap":{"0":{"loc":{"start":{"line":16,"column":6},"end":{"line":16,"column":14}},"type":"default-arg","locations":[{"start":{"line":16,"column":13},"end":{"line":16,"column":14}}],"line":16},"1":{"loc":{"start":{"line":17,"column":6},"end":{"line":17,"column":16}},"type":"default-arg","locations":[{"start":{"line":17,"column":14},"end":{"line":17,"column":16}}],"line":17},"2":{"loc":{"start":{"line":39,"column":4},"end":{"line":43,"column":5}},"type":"if","locations":[{"start":{"line":39,"column":4},"end":{"line":43,"column":5}},{"start":{},"end":{}}],"line":39},"3":{"loc":{"start":{"line":45,"column":4},"end":{"line":49,"column":5}},"type":"if","locations":[{"start":{"line":45,"column":4},"end":{"line":49,"column":5}},{"start":{},"end":{}}],"line":45},"4":{"loc":{"start":{"line":51,"column":4},"end":{"line":55,"column":5}},"type":"if","locations":[{"start":{"line":51,"column":4},"end":{"line":55,"column":5}},{"start":{},"end":{}}],"line":51},"5":{"loc":{"start":{"line":57,"column":4},"end":{"line":61,"column":5}},"type":"if","locations":[{"start":{"line":57,"column":4},"end":{"line":61,"column":5}},{"start":{},"end":{}}],"line":57},"6":{"loc":{"start":{"line":64,"column":4},"end":{"line":86,"column":5}},"type":"if","locations":[{"start":{"line":64,"column":4},"end":{"line":86,"column":5}},{"start":{"line":75,"column":11},"end":{"line":86,"column":5}}],"line":64},"7":{"loc":{"start":{"line":70,"column":6},"end":{"line":74,"column":7}},"type":"if","locations":[{"start":{"line":70,"column":6},"end":{"line":74,"column":7}},{"start":{},"end":{}}],"line":70},"8":{"loc":{"start":{"line":75,"column":11},"end":{"line":86,"column":5}},"type":"if","locations":[{"start":{"line":75,"column":11},"end":{"line":86,"column":5}},{"start":{},"end":{}}],"line":75},"9":{"loc":{"start":{"line":81,"column":6},"end":{"line":85,"column":7}},"type":"if","locations":[{"start":{"line":81,"column":6},"end":{"line":85,"column":7}},{"start":{},"end":{}}],"line":81},"10":{"loc":{"start":{"line":88,"column":4},"end":{"line":90,"column":5}},"type":"if","locations":[{"start":{"line":88,"column":4},"end":{"line":90,"column":5}},{"start":{},"end":{}}],"line":88},"11":{"loc":{"start":{"line":117,"column":4},"end":{"line":121,"column":5}},"type":"if","locations":[{"start":{"line":117,"column":4},"end":{"line":121,"column":5}},{"start":{},"end":{}}],"line":117},"12":{"loc":{"start":{"line":123,"column":4},"end":{"line":127,"column":5}},"type":"if","locations":[{"start":{"line":123,"column":4},"end":{"line":127,"column":5}},{"start":{},"end":{}}],"line":123},"13":{"loc":{"start":{"line":129,"column":4},"end":{"line":133,"column":5}},"type":"if","locations":[{"start":{"line":129,"column":4},"end":{"line":133,"column":5}},{"start":{},"end":{}}],"line":129},"14":{"loc":{"start":{"line":135,"column":4},"end":{"line":139,"column":5}},"type":"if","locations":[{"start":{"line":135,"column":4},"end":{"line":139,"column":5}},{"start":{},"end":{}}],"line":135},"15":{"loc":{"start":{"line":142,"column":4},"end":{"line":162,"column":5}},"type":"if","locations":[{"start":{"line":142,"column":4},"end":{"line":162,"column":5}},{"start":{"line":152,"column":11},"end":{"line":162,"column":5}}],"line":142},"16":{"loc":{"start":{"line":147,"column":6},"end":{"line":151,"column":7}},"type":"if","locations":[{"start":{"line":147,"column":6},"end":{"line":151,"column":7}},{"start":{},"end":{}}],"line":147},"17":{"loc":{"start":{"line":152,"column":11},"end":{"line":162,"column":5}},"type":"if","locations":[{"start":{"line":152,"column":11},"end":{"line":162,"column":5}},{"start":{},"end":{}}],"line":152},"18":{"loc":{"start":{"line":157,"column":6},"end":{"line":161,"column":7}},"type":"if","locations":[{"start":{"line":157,"column":6},"end":{"line":161,"column":7}},{"start":{},"end":{}}],"line":157},"19":{"loc":{"start":{"line":164,"column":4},"end":{"line":166,"column":5}},"type":"if","locations":[{"start":{"line":164,"column":4},"end":{"line":166,"column":5}},{"start":{},"end":{}}],"line":164},"20":{"loc":{"start":{"line":208,"column":4},"end":{"line":210,"column":5}},"type":"if","locations":[{"start":{"line":208,"column":4},"end":{"line":210,"column":5}},{"start":{},"end":{}}],"line":208},"21":{"loc":{"start":{"line":215,"column":4},"end":{"line":231,"column":5}},"type":"if","locations":[{"start":{"line":215,"column":4},"end":{"line":231,"column":5}},{"start":{"line":223,"column":11},"end":{"line":231,"column":5}}],"line":215},"22":{"loc":{"start":{"line":220,"column":6},"end":{"line":222,"column":7}},"type":"if","locations":[{"start":{"line":220,"column":6},"end":{"line":222,"column":7}},{"start":{},"end":{}}],"line":220},"23":{"loc":{"start":{"line":220,"column":10},"end":{"line":220,"column":102}},"type":"binary-expr","locations":[{"start":{"line":220,"column":10},"end":{"line":220,"column":43}},{"start":{"line":220,"column":47},"end":{"line":220,"column":102}}],"line":220},"24":{"loc":{"start":{"line":223,"column":11},"end":{"line":231,"column":5}},"type":"if","locations":[{"start":{"line":223,"column":11},"end":{"line":231,"column":5}},{"start":{},"end":{}}],"line":223},"25":{"loc":{"start":{"line":228,"column":6},"end":{"line":230,"column":7}},"type":"if","locations":[{"start":{"line":228,"column":6},"end":{"line":230,"column":7}},{"start":{},"end":{}}],"line":228},"26":{"loc":{"start":{"line":228,"column":10},"end":{"line":228,"column":99}},"type":"binary-expr","locations":[{"start":{"line":228,"column":10},"end":{"line":228,"column":42}},{"start":{"line":228,"column":46},"end":{"line":228,"column":99}}],"line":228},"27":{"loc":{"start":{"line":248,"column":4},"end":{"line":250,"column":5}},"type":"if","locations":[{"start":{"line":248,"column":4},"end":{"line":250,"column":5}},{"start":{},"end":{}}],"line":248},"28":{"loc":{"start":{"line":260,"column":4},"end":{"line":262,"column":5}},"type":"if","locations":[{"start":{"line":260,"column":4},"end":{"line":262,"column":5}},{"start":{},"end":{}}],"line":260},"29":{"loc":{"start":{"line":272,"column":4},"end":{"line":274,"column":5}},"type":"if","locations":[{"start":{"line":272,"column":4},"end":{"line":274,"column":5}},{"start":{},"end":{}}],"line":272},"30":{"loc":{"start":{"line":276,"column":4},"end":{"line":278,"column":5}},"type":"if","locations":[{"start":{"line":276,"column":4},"end":{"line":278,"column":5}},{"start":{},"end":{}}],"line":276},"31":{"loc":{"start":{"line":286,"column":4},"end":{"line":288,"column":5}},"type":"if","locations":[{"start":{"line":286,"column":4},"end":{"line":288,"column":5}},{"start":{},"end":{}}],"line":286},"32":{"loc":{"start":{"line":312,"column":4},"end":{"line":314,"column":5}},"type":"if","locations":[{"start":{"line":312,"column":4},"end":{"line":314,"column":5}},{"start":{},"end":{}}],"line":312},"33":{"loc":{"start":{"line":328,"column":4},"end":{"line":330,"column":5}},"type":"if","locations":[{"start":{"line":328,"column":4},"end":{"line":330,"column":5}},{"start":{},"end":{}}],"line":328},"34":{"loc":{"start":{"line":335,"column":4},"end":{"line":353,"column":5}},"type":"if","locations":[{"start":{"line":335,"column":4},"end":{"line":353,"column":5}},{"start":{"line":347,"column":11},"end":{"line":353,"column":5}}],"line":335},"35":{"loc":{"start":{"line":340,"column":6},"end":{"line":342,"column":7}},"type":"if","locations":[{"start":{"line":340,"column":6},"end":{"line":342,"column":7}},{"start":{},"end":{}}],"line":340},"36":{"loc":{"start":{"line":340,"column":10},"end":{"line":340,"column":102}},"type":"binary-expr","locations":[{"start":{"line":340,"column":10},"end":{"line":340,"column":43}},{"start":{"line":340,"column":47},"end":{"line":340,"column":102}}],"line":340},"37":{"loc":{"start":{"line":344,"column":6},"end":{"line":346,"column":7}},"type":"if","locations":[{"start":{"line":344,"column":6},"end":{"line":346,"column":7}},{"start":{},"end":{}}],"line":344},"38":{"loc":{"start":{"line":347,"column":11},"end":{"line":353,"column":5}},"type":"if","locations":[{"start":{"line":347,"column":11},"end":{"line":353,"column":5}},{"start":{"line":351,"column":11},"end":{"line":353,"column":5}}],"line":347},"39":{"loc":{"start":{"line":348,"column":6},"end":{"line":350,"column":7}},"type":"if","locations":[{"start":{"line":348,"column":6},"end":{"line":350,"column":7}},{"start":{},"end":{}}],"line":348},"40":{"loc":{"start":{"line":351,"column":11},"end":{"line":353,"column":5}},"type":"if","locations":[{"start":{"line":351,"column":11},"end":{"line":353,"column":5}},{"start":{},"end":{}}],"line":351},"41":{"loc":{"start":{"line":351,"column":15},"end":{"line":351,"column":73}},"type":"binary-expr","locations":[{"start":{"line":351,"column":15},"end":{"line":351,"column":40}},{"start":{"line":351,"column":44},"end":{"line":351,"column":73}}],"line":351},"42":{"loc":{"start":{"line":376,"column":4},"end":{"line":378,"column":5}},"type":"if","locations":[{"start":{"line":376,"column":4},"end":{"line":378,"column":5}},{"start":{},"end":{}}],"line":376},"43":{"loc":{"start":{"line":392,"column":4},"end":{"line":394,"column":5}},"type":"if","locations":[{"start":{"line":392,"column":4},"end":{"line":394,"column":5}},{"start":{},"end":{}}],"line":392},"44":{"loc":{"start":{"line":399,"column":4},"end":{"line":407,"column":5}},"type":"if","locations":[{"start":{"line":399,"column":4},"end":{"line":407,"column":5}},{"start":{"line":401,"column":11},"end":{"line":407,"column":5}}],"line":399},"45":{"loc":{"start":{"line":401,"column":11},"end":{"line":407,"column":5}},"type":"if","locations":[{"start":{"line":401,"column":11},"end":{"line":407,"column":5}},{"start":{"line":405,"column":11},"end":{"line":407,"column":5}}],"line":401},"46":{"loc":{"start":{"line":402,"column":6},"end":{"line":404,"column":7}},"type":"if","locations":[{"start":{"line":402,"column":6},"end":{"line":404,"column":7}},{"start":{},"end":{}}],"line":402},"47":{"loc":{"start":{"line":405,"column":11},"end":{"line":407,"column":5}},"type":"if","locations":[{"start":{"line":405,"column":11},"end":{"line":407,"column":5}},{"start":{},"end":{}}],"line":405},"48":{"loc":{"start":{"line":405,"column":15},"end":{"line":405,"column":73}},"type":"binary-expr","locations":[{"start":{"line":405,"column":15},"end":{"line":405,"column":40}},{"start":{"line":405,"column":44},"end":{"line":405,"column":73}}],"line":405}},"s":{"0":7,"1":7,"2":7,"3":7,"4":7,"5":7,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":0,"14":0,"15":0,"16":1,"17":0,"18":0,"19":0,"20":1,"21":0,"22":0,"23":0,"24":1,"25":0,"26":0,"27":0,"28":1,"29":1,"30":1,"31":1,"32":1,"33":1,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":1,"41":1,"42":1,"43":1,"44":1,"45":1,"46":1,"47":1,"48":1,"49":1,"50":1,"51":1,"52":1,"53":1,"54":1,"55":1,"56":0,"57":0,"58":0,"59":1,"60":0,"61":0,"62":0,"63":1,"64":0,"65":0,"66":0,"67":1,"68":0,"69":0,"70":0,"71":1,"72":1,"73":1,"74":1,"75":1,"76":1,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":1,"84":1,"85":1,"86":1,"87":0,"88":0,"89":7,"90":1,"91":1,"92":1,"93":1,"94":0,"95":1,"96":1,"97":0,"98":0,"99":0,"100":1,"101":1,"102":1,"103":0,"104":1,"105":0,"106":0,"107":7,"108":1,"109":1,"110":1,"111":0,"112":1,"113":1,"114":1,"115":0,"116":1,"117":1,"118":1,"119":0,"120":1,"121":0,"122":1,"123":1,"124":0,"125":1,"126":1,"127":0,"128":0,"129":7,"130":1,"131":1,"132":1,"133":0,"134":1,"135":1,"136":1,"137":1,"138":0,"139":1,"140":1,"141":0,"142":0,"143":0,"144":0,"145":0,"146":1,"147":1,"148":0,"149":0,"150":0,"151":1,"152":1,"153":0,"154":0,"155":7,"156":1,"157":1,"158":1,"159":0,"160":1,"161":1,"162":1,"163":1,"164":0,"165":1,"166":1,"167":0,"168":1,"169":1,"170":0,"171":0,"172":0,"173":1,"174":1,"175":0,"176":0,"177":7},"f":{"0":1,"1":1,"2":1,"3":1,"4":1},"b":{"0":[1],"1":[1],"2":[0,1],"3":[0,1],"4":[0,1],"5":[0,1],"6":[1,0],"7":[1,0],"8":[0,0],"9":[0,0],"10":[1,0],"11":[0,1],"12":[0,1],"13":[0,1],"14":[0,1],"15":[1,0],"16":[1,0],"17":[0,0],"18":[0,0],"19":[1,0],"20":[0,1],"21":[0,1],"22":[0,0],"23":[0,0],"24":[1,0],"25":[0,1],"26":[1,1],"27":[0,1],"28":[0,1],"29":[0,1],"30":[0,1],"31":[0,1],"32":[0,1],"33":[0,1],"34":[0,1],"35":[0,0],"36":[0,0],"37":[0,0],"38":[1,0],"39":[0,1],"40":[0,0],"41":[0,0],"42":[0,1],"43":[0,1],"44":[0,1],"45":[1,0],"46":[0,1],"47":[0,0],"48":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"5d67cc097cc6beaf71c148fdd3a6964e646da425"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/routes/auth.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/routes/auth.js","statementMap":{"0":{"start":{"line":1,"column":16},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":15},"end":{"line":2,"column":34}},"2":{"start":{"line":3,"column":12},"end":{"line":3,"column":35}},"3":{"start":{"line":4,"column":35},"end":{"line":4,"column":63}},"4":{"start":{"line":5,"column":13},"end":{"line":5,"column":46}},"5":{"start":{"line":6,"column":30},"end":{"line":6,"column":59}},"6":{"start":{"line":7,"column":15},"end":{"line":7,"column":35}},"7":{"start":{"line":9,"column":15},"end":{"line":9,"column":31}},"8":{"start":{"line":12,"column":0},"end":{"line":70,"column":3}},"9":{"start":{"line":19,"column":2},"end":{"line":69,"column":3}},"10":{"start":{"line":20,"column":19},"end":{"line":20,"column":40}},"11":{"start":{"line":21,"column":4},"end":{"line":23,"column":5}},"12":{"start":{"line":22,"column":6},"end":{"line":22,"column":62}},"13":{"start":{"line":25,"column":59},"end":{"line":25,"column":67}},"14":{"start":{"line":28,"column":25},"end":{"line":31,"column":5}},"15":{"start":{"line":33,"column":4},"end":{"line":35,"column":5}},"16":{"start":{"line":34,"column":6},"end":{"line":34,"column":68}},"17":{"start":{"line":38,"column":25},"end":{"line":38,"column":56}},"18":{"start":{"line":41,"column":23},"end":{"line":44,"column":5}},"19":{"start":{"line":46,"column":17},"end":{"line":46,"column":35}},"20":{"start":{"line":49,"column":18},"end":{"line":53,"column":5}},"21":{"start":{"line":55,"column":4},"end":{"line":65,"column":7}},"22":{"start":{"line":67,"column":4},"end":{"line":67,"column":48}},"23":{"start":{"line":68,"column":4},"end":{"line":68,"column":59}},"24":{"start":{"line":73,"column":0},"end":{"line":129,"column":3}},"25":{"start":{"line":77,"column":2},"end":{"line":128,"column":3}},"26":{"start":{"line":78,"column":19},"end":{"line":78,"column":40}},"27":{"start":{"line":79,"column":4},"end":{"line":81,"column":5}},"28":{"start":{"line":80,"column":6},"end":{"line":80,"column":62}},"29":{"start":{"line":83,"column":32},"end":{"line":83,"column":40}},"30":{"start":{"line":86,"column":23},"end":{"line":89,"column":5}},"31":{"start":{"line":91,"column":4},"end":{"line":93,"column":5}},"32":{"start":{"line":92,"column":6},"end":{"line":92,"column":68}},"33":{"start":{"line":95,"column":17},"end":{"line":95,"column":35}},"34":{"start":{"line":97,"column":4},"end":{"line":99,"column":5}},"35":{"start":{"line":98,"column":6},"end":{"line":98,"column":68}},"36":{"start":{"line":102,"column":28},"end":{"line":102,"column":78}},"37":{"start":{"line":103,"column":4},"end":{"line":105,"column":5}},"38":{"start":{"line":104,"column":6},"end":{"line":104,"column":68}},"39":{"start":{"line":108,"column":18},"end":{"line":112,"column":5}},"40":{"start":{"line":114,"column":4},"end":{"line":124,"column":7}},"41":{"start":{"line":126,"column":4},"end":{"line":126,"column":41}},"42":{"start":{"line":127,"column":4},"end":{"line":127,"column":52}},"43":{"start":{"line":132,"column":0},"end":{"line":147,"column":3}},"44":{"start":{"line":133,"column":2},"end":{"line":146,"column":3}},"45":{"start":{"line":134,"column":4},"end":{"line":142,"column":7}},"46":{"start":{"line":144,"column":4},"end":{"line":144,"column":44}},"47":{"start":{"line":145,"column":4},"end":{"line":145,"column":70}},"48":{"start":{"line":150,"column":0},"end":{"line":152,"column":3}},"49":{"start":{"line":151,"column":2},"end":{"line":151,"column":45}},"50":{"start":{"line":154,"column":0},"end":{"line":154,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":18,"column":3},"end":{"line":18,"column":4}},"loc":{"start":{"line":18,"column":23},"end":{"line":70,"column":1}},"line":18},"1":{"name":"(anonymous_1)","decl":{"start":{"line":76,"column":3},"end":{"line":76,"column":4}},"loc":{"start":{"line":76,"column":23},"end":{"line":129,"column":1}},"line":76},"2":{"name":"(anonymous_2)","decl":{"start":{"line":132,"column":37},"end":{"line":132,"column":38}},"loc":{"start":{"line":132,"column":57},"end":{"line":147,"column":1}},"line":132},"3":{"name":"(anonymous_3)","decl":{"start":{"line":150,"column":42},"end":{"line":150,"column":43}},"loc":{"start":{"line":150,"column":56},"end":{"line":152,"column":1}},"line":150}},"branchMap":{"0":{"loc":{"start":{"line":21,"column":4},"end":{"line":23,"column":5}},"type":"if","locations":[{"start":{"line":21,"column":4},"end":{"line":23,"column":5}},{"start":{},"end":{}}],"line":21},"1":{"loc":{"start":{"line":33,"column":4},"end":{"line":35,"column":5}},"type":"if","locations":[{"start":{"line":33,"column":4},"end":{"line":35,"column":5}},{"start":{},"end":{}}],"line":33},"2":{"loc":{"start":{"line":79,"column":4},"end":{"line":81,"column":5}},"type":"if","locations":[{"start":{"line":79,"column":4},"end":{"line":81,"column":5}},{"start":{},"end":{}}],"line":79},"3":{"loc":{"start":{"line":91,"column":4},"end":{"line":93,"column":5}},"type":"if","locations":[{"start":{"line":91,"column":4},"end":{"line":93,"column":5}},{"start":{},"end":{}}],"line":91},"4":{"loc":{"start":{"line":97,"column":4},"end":{"line":99,"column":5}},"type":"if","locations":[{"start":{"line":97,"column":4},"end":{"line":99,"column":5}},{"start":{},"end":{}}],"line":97},"5":{"loc":{"start":{"line":103,"column":4},"end":{"line":105,"column":5}},"type":"if","locations":[{"start":{"line":103,"column":4},"end":{"line":105,"column":5}},{"start":{},"end":{}}],"line":103}},"s":{"0":7,"1":7,"2":7,"3":7,"4":7,"5":7,"6":7,"7":7,"8":7,"9":25,"10":25,"11":25,"12":1,"13":24,"14":24,"15":24,"16":1,"17":23,"18":23,"19":23,"20":23,"21":23,"22":0,"23":0,"24":7,"25":4,"26":4,"27":4,"28":0,"29":4,"30":4,"31":4,"32":1,"33":3,"34":3,"35":0,"36":3,"37":3,"38":1,"39":2,"40":2,"41":0,"42":0,"43":7,"44":1,"45":1,"46":0,"47":0,"48":7,"49":0,"50":7},"f":{"0":25,"1":4,"2":1,"3":0},"b":{"0":[1,24],"1":[1,23],"2":[0,4],"3":[1,3],"4":[0,3],"5":[1,2]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"b3ce43cbe64a8c808ab6f56e9bf69054c0a696ed"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/routes/candidates.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/routes/candidates.js","statementMap":{"0":{"start":{"line":1,"column":16},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":35},"end":{"line":2,"column":63}},"2":{"start":{"line":3,"column":13},"end":{"line":3,"column":46}},"3":{"start":{"line":4,"column":43},"end":{"line":4,"column":72}},"4":{"start":{"line":6,"column":15},"end":{"line":6,"column":31}},"5":{"start":{"line":9,"column":0},"end":{"line":106,"column":3}},"6":{"start":{"line":10,"column":2},"end":{"line":105,"column":3}},"7":{"start":{"line":11,"column":72},"end":{"line":11,"column":81}},"8":{"start":{"line":13,"column":16},"end":{"line":17,"column":5}},"9":{"start":{"line":18,"column":24},"end":{"line":18,"column":26}},"10":{"start":{"line":19,"column":21},"end":{"line":19,"column":22}},"11":{"start":{"line":20,"column":23},"end":{"line":20,"column":25}},"12":{"start":{"line":22,"column":4},"end":{"line":27,"column":5}},"13":{"start":{"line":23,"column":25},"end":{"line":23,"column":61}},"14":{"start":{"line":23,"column":52},"end":{"line":23,"column":60}},"15":{"start":{"line":24,"column":6},"end":{"line":24,"column":19}},"16":{"start":{"line":25,"column":6},"end":{"line":25,"column":52}},"17":{"start":{"line":26,"column":6},"end":{"line":26,"column":35}},"18":{"start":{"line":29,"column":4},"end":{"line":33,"column":5}},"19":{"start":{"line":30,"column":6},"end":{"line":30,"column":19}},"20":{"start":{"line":31,"column":6},"end":{"line":31,"column":61}},"21":{"start":{"line":32,"column":6},"end":{"line":32,"column":40}},"22":{"start":{"line":35,"column":4},"end":{"line":39,"column":5}},"23":{"start":{"line":36,"column":6},"end":{"line":36,"column":19}},"24":{"start":{"line":37,"column":6},"end":{"line":37,"column":57}},"25":{"start":{"line":38,"column":6},"end":{"line":38,"column":40}},"26":{"start":{"line":41,"column":4},"end":{"line":43,"column":5}},"27":{"start":{"line":42,"column":6},"end":{"line":42,"column":52}},"28":{"start":{"line":45,"column":4},"end":{"line":45,"column":43}},"29":{"start":{"line":48,"column":19},"end":{"line":48,"column":37}},"30":{"start":{"line":49,"column":4},"end":{"line":49,"column":17}},"31":{"start":{"line":50,"column":4},"end":{"line":50,"column":37}},"32":{"start":{"line":51,"column":4},"end":{"line":51,"column":28}},"33":{"start":{"line":52,"column":4},"end":{"line":52,"column":17}},"34":{"start":{"line":53,"column":4},"end":{"line":53,"column":38}},"35":{"start":{"line":54,"column":4},"end":{"line":54,"column":29}},"36":{"start":{"line":56,"column":19},"end":{"line":56,"column":55}},"37":{"start":{"line":59,"column":21},"end":{"line":63,"column":5}},"38":{"start":{"line":64,"column":24},"end":{"line":64,"column":26}},"39":{"start":{"line":65,"column":26},"end":{"line":65,"column":27}},"40":{"start":{"line":66,"column":28},"end":{"line":66,"column":30}},"41":{"start":{"line":68,"column":4},"end":{"line":73,"column":5}},"42":{"start":{"line":69,"column":25},"end":{"line":69,"column":61}},"43":{"start":{"line":69,"column":52},"end":{"line":69,"column":60}},"44":{"start":{"line":70,"column":6},"end":{"line":70,"column":24}},"45":{"start":{"line":71,"column":6},"end":{"line":71,"column":62}},"46":{"start":{"line":72,"column":6},"end":{"line":72,"column":35}},"47":{"start":{"line":75,"column":4},"end":{"line":79,"column":5}},"48":{"start":{"line":76,"column":6},"end":{"line":76,"column":24}},"49":{"start":{"line":77,"column":6},"end":{"line":77,"column":71}},"50":{"start":{"line":78,"column":6},"end":{"line":78,"column":40}},"51":{"start":{"line":81,"column":4},"end":{"line":85,"column":5}},"52":{"start":{"line":82,"column":6},"end":{"line":82,"column":24}},"53":{"start":{"line":83,"column":6},"end":{"line":83,"column":67}},"54":{"start":{"line":84,"column":6},"end":{"line":84,"column":40}},"55":{"start":{"line":87,"column":4},"end":{"line":89,"column":5}},"56":{"start":{"line":88,"column":6},"end":{"line":88,"column":62}},"57":{"start":{"line":91,"column":24},"end":{"line":91,"column":65}},"58":{"start":{"line":93,"column":4},"end":{"line":101,"column":7}},"59":{"start":{"line":103,"column":4},"end":{"line":103,"column":50}},"60":{"start":{"line":104,"column":4},"end":{"line":104,"column":66}},"61":{"start":{"line":109,"column":0},"end":{"line":129,"column":3}},"62":{"start":{"line":110,"column":2},"end":{"line":128,"column":3}},"63":{"start":{"line":111,"column":19},"end":{"line":111,"column":29}},"64":{"start":{"line":113,"column":19},"end":{"line":118,"column":12}},"65":{"start":{"line":120,"column":4},"end":{"line":122,"column":5}},"66":{"start":{"line":121,"column":6},"end":{"line":121,"column":68}},"67":{"start":{"line":124,"column":4},"end":{"line":124,"column":29}},"68":{"start":{"line":126,"column":4},"end":{"line":126,"column":49}},"69":{"start":{"line":127,"column":4},"end":{"line":127,"column":65}},"70":{"start":{"line":132,"column":0},"end":{"line":187,"column":3}},"71":{"start":{"line":144,"column":2},"end":{"line":186,"column":3}},"72":{"start":{"line":145,"column":19},"end":{"line":145,"column":40}},"73":{"start":{"line":146,"column":4},"end":{"line":148,"column":5}},"74":{"start":{"line":147,"column":6},"end":{"line":147,"column":62}},"75":{"start":{"line":161,"column":8},"end":{"line":161,"column":16}},"76":{"start":{"line":164,"column":30},"end":{"line":167,"column":5}},"77":{"start":{"line":169,"column":4},"end":{"line":171,"column":5}},"78":{"start":{"line":170,"column":6},"end":{"line":170,"column":81}},"79":{"start":{"line":173,"column":19},"end":{"line":177,"column":139}},"80":{"start":{"line":179,"column":4},"end":{"line":182,"column":7}},"81":{"start":{"line":184,"column":4},"end":{"line":184,"column":52}},"82":{"start":{"line":185,"column":4},"end":{"line":185,"column":74}},"83":{"start":{"line":190,"column":0},"end":{"line":309,"column":3}},"84":{"start":{"line":202,"column":2},"end":{"line":308,"column":3}},"85":{"start":{"line":203,"column":19},"end":{"line":203,"column":40}},"86":{"start":{"line":204,"column":4},"end":{"line":206,"column":5}},"87":{"start":{"line":205,"column":6},"end":{"line":205,"column":62}},"88":{"start":{"line":208,"column":19},"end":{"line":208,"column":29}},"89":{"start":{"line":220,"column":8},"end":{"line":220,"column":16}},"90":{"start":{"line":223,"column":28},"end":{"line":226,"column":5}},"91":{"start":{"line":228,"column":4},"end":{"line":230,"column":5}},"92":{"start":{"line":229,"column":6},"end":{"line":229,"column":68}},"93":{"start":{"line":233,"column":4},"end":{"line":235,"column":5}},"94":{"start":{"line":234,"column":6},"end":{"line":234,"column":62}},"95":{"start":{"line":237,"column":25},"end":{"line":237,"column":27}},"96":{"start":{"line":238,"column":25},"end":{"line":238,"column":27}},"97":{"start":{"line":239,"column":21},"end":{"line":239,"column":22}},"98":{"start":{"line":241,"column":4},"end":{"line":245,"column":5}},"99":{"start":{"line":242,"column":6},"end":{"line":242,"column":50}},"100":{"start":{"line":243,"column":6},"end":{"line":243,"column":31}},"101":{"start":{"line":244,"column":6},"end":{"line":244,"column":19}},"102":{"start":{"line":246,"column":4},"end":{"line":250,"column":5}},"103":{"start":{"line":247,"column":6},"end":{"line":247,"column":53}},"104":{"start":{"line":248,"column":6},"end":{"line":248,"column":34}},"105":{"start":{"line":249,"column":6},"end":{"line":249,"column":19}},"106":{"start":{"line":251,"column":4},"end":{"line":255,"column":5}},"107":{"start":{"line":252,"column":6},"end":{"line":252,"column":57}},"108":{"start":{"line":253,"column":6},"end":{"line":253,"column":37}},"109":{"start":{"line":254,"column":6},"end":{"line":254,"column":19}},"110":{"start":{"line":256,"column":4},"end":{"line":260,"column":5}},"111":{"start":{"line":257,"column":6},"end":{"line":257,"column":55}},"112":{"start":{"line":258,"column":6},"end":{"line":258,"column":35}},"113":{"start":{"line":259,"column":6},"end":{"line":259,"column":19}},"114":{"start":{"line":261,"column":4},"end":{"line":265,"column":5}},"115":{"start":{"line":262,"column":6},"end":{"line":262,"column":58}},"116":{"start":{"line":263,"column":6},"end":{"line":263,"column":38}},"117":{"start":{"line":264,"column":6},"end":{"line":264,"column":19}},"118":{"start":{"line":266,"column":4},"end":{"line":270,"column":5}},"119":{"start":{"line":267,"column":6},"end":{"line":267,"column":48}},"120":{"start":{"line":268,"column":6},"end":{"line":268,"column":29}},"121":{"start":{"line":269,"column":6},"end":{"line":269,"column":19}},"122":{"start":{"line":271,"column":4},"end":{"line":275,"column":5}},"123":{"start":{"line":272,"column":6},"end":{"line":272,"column":51}},"124":{"start":{"line":273,"column":6},"end":{"line":273,"column":32}},"125":{"start":{"line":274,"column":6},"end":{"line":274,"column":19}},"126":{"start":{"line":276,"column":4},"end":{"line":280,"column":5}},"127":{"start":{"line":277,"column":6},"end":{"line":277,"column":61}},"128":{"start":{"line":278,"column":6},"end":{"line":278,"column":41}},"129":{"start":{"line":279,"column":6},"end":{"line":279,"column":19}},"130":{"start":{"line":281,"column":4},"end":{"line":285,"column":5}},"131":{"start":{"line":282,"column":6},"end":{"line":282,"column":57}},"132":{"start":{"line":283,"column":6},"end":{"line":283,"column":38}},"133":{"start":{"line":284,"column":6},"end":{"line":284,"column":19}},"134":{"start":{"line":286,"column":4},"end":{"line":290,"column":5}},"135":{"start":{"line":287,"column":6},"end":{"line":287,"column":63}},"136":{"start":{"line":288,"column":6},"end":{"line":288,"column":43}},"137":{"start":{"line":289,"column":6},"end":{"line":289,"column":19}},"138":{"start":{"line":292,"column":4},"end":{"line":294,"column":5}},"139":{"start":{"line":293,"column":6},"end":{"line":293,"column":68}},"140":{"start":{"line":296,"column":4},"end":{"line":296,"column":26}},"141":{"start":{"line":297,"column":18},"end":{"line":297,"column":138}},"142":{"start":{"line":299,"column":19},"end":{"line":299,"column":56}},"143":{"start":{"line":301,"column":4},"end":{"line":304,"column":7}},"144":{"start":{"line":306,"column":4},"end":{"line":306,"column":52}},"145":{"start":{"line":307,"column":4},"end":{"line":307,"column":74}},"146":{"start":{"line":312,"column":0},"end":{"line":372,"column":3}},"147":{"start":{"line":313,"column":2},"end":{"line":371,"column":3}},"148":{"start":{"line":314,"column":19},"end":{"line":314,"column":29}},"149":{"start":{"line":315,"column":45},"end":{"line":315,"column":54}},"150":{"start":{"line":317,"column":16},"end":{"line":323,"column":5}},"151":{"start":{"line":324,"column":24},"end":{"line":324,"column":28}},"152":{"start":{"line":325,"column":21},"end":{"line":325,"column":22}},"153":{"start":{"line":327,"column":4},"end":{"line":331,"column":5}},"154":{"start":{"line":328,"column":6},"end":{"line":328,"column":19}},"155":{"start":{"line":329,"column":6},"end":{"line":329,"column":48}},"156":{"start":{"line":330,"column":6},"end":{"line":330,"column":31}},"157":{"start":{"line":333,"column":4},"end":{"line":333,"column":43}},"158":{"start":{"line":336,"column":19},"end":{"line":336,"column":37}},"159":{"start":{"line":337,"column":4},"end":{"line":337,"column":17}},"160":{"start":{"line":338,"column":4},"end":{"line":338,"column":37}},"161":{"start":{"line":339,"column":4},"end":{"line":339,"column":28}},"162":{"start":{"line":340,"column":4},"end":{"line":340,"column":17}},"163":{"start":{"line":341,"column":4},"end":{"line":341,"column":38}},"164":{"start":{"line":342,"column":4},"end":{"line":342,"column":29}},"165":{"start":{"line":344,"column":19},"end":{"line":344,"column":55}},"166":{"start":{"line":347,"column":21},"end":{"line":351,"column":5}},"167":{"start":{"line":352,"column":24},"end":{"line":352,"column":28}},"168":{"start":{"line":353,"column":4},"end":{"line":356,"column":5}},"169":{"start":{"line":354,"column":6},"end":{"line":354,"column":41}},"170":{"start":{"line":355,"column":6},"end":{"line":355,"column":31}},"171":{"start":{"line":357,"column":24},"end":{"line":357,"column":65}},"172":{"start":{"line":359,"column":4},"end":{"line":367,"column":7}},"173":{"start":{"line":369,"column":4},"end":{"line":369,"column":62}},"174":{"start":{"line":370,"column":4},"end":{"line":370,"column":78}},"175":{"start":{"line":374,"column":0},"end":{"line":374,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":9,"column":35},"end":{"line":9,"column":36}},"loc":{"start":{"line":9,"column":55},"end":{"line":106,"column":1}},"line":9},"1":{"name":"(anonymous_1)","decl":{"start":{"line":23,"column":47},"end":{"line":23,"column":48}},"loc":{"start":{"line":23,"column":52},"end":{"line":23,"column":60}},"line":23},"2":{"name":"(anonymous_2)","decl":{"start":{"line":69,"column":47},"end":{"line":69,"column":48}},"loc":{"start":{"line":69,"column":52},"end":{"line":69,"column":60}},"line":69},"3":{"name":"(anonymous_3)","decl":{"start":{"line":109,"column":38},"end":{"line":109,"column":39}},"loc":{"start":{"line":109,"column":58},"end":{"line":129,"column":1}},"line":109},"4":{"name":"(anonymous_4)","decl":{"start":{"line":143,"column":3},"end":{"line":143,"column":4}},"loc":{"start":{"line":143,"column":23},"end":{"line":187,"column":1}},"line":143},"5":{"name":"(anonymous_5)","decl":{"start":{"line":201,"column":3},"end":{"line":201,"column":4}},"loc":{"start":{"line":201,"column":23},"end":{"line":309,"column":1}},"line":201},"6":{"name":"(anonymous_6)","decl":{"start":{"line":312,"column":51},"end":{"line":312,"column":52}},"loc":{"start":{"line":312,"column":71},"end":{"line":372,"column":1}},"line":312}},"branchMap":{"0":{"loc":{"start":{"line":11,"column":47},"end":{"line":11,"column":55}},"type":"default-arg","locations":[{"start":{"line":11,"column":54},"end":{"line":11,"column":55}}],"line":11},"1":{"loc":{"start":{"line":11,"column":57},"end":{"line":11,"column":67}},"type":"default-arg","locations":[{"start":{"line":11,"column":65},"end":{"line":11,"column":67}}],"line":11},"2":{"loc":{"start":{"line":22,"column":4},"end":{"line":27,"column":5}},"type":"if","locations":[{"start":{"line":22,"column":4},"end":{"line":27,"column":5}},{"start":{},"end":{}}],"line":22},"3":{"loc":{"start":{"line":29,"column":4},"end":{"line":33,"column":5}},"type":"if","locations":[{"start":{"line":29,"column":4},"end":{"line":33,"column":5}},{"start":{},"end":{}}],"line":29},"4":{"loc":{"start":{"line":35,"column":4},"end":{"line":39,"column":5}},"type":"if","locations":[{"start":{"line":35,"column":4},"end":{"line":39,"column":5}},{"start":{},"end":{}}],"line":35},"5":{"loc":{"start":{"line":41,"column":4},"end":{"line":43,"column":5}},"type":"if","locations":[{"start":{"line":41,"column":4},"end":{"line":43,"column":5}},{"start":{},"end":{}}],"line":41},"6":{"loc":{"start":{"line":68,"column":4},"end":{"line":73,"column":5}},"type":"if","locations":[{"start":{"line":68,"column":4},"end":{"line":73,"column":5}},{"start":{},"end":{}}],"line":68},"7":{"loc":{"start":{"line":75,"column":4},"end":{"line":79,"column":5}},"type":"if","locations":[{"start":{"line":75,"column":4},"end":{"line":79,"column":5}},{"start":{},"end":{}}],"line":75},"8":{"loc":{"start":{"line":81,"column":4},"end":{"line":85,"column":5}},"type":"if","locations":[{"start":{"line":81,"column":4},"end":{"line":85,"column":5}},{"start":{},"end":{}}],"line":81},"9":{"loc":{"start":{"line":87,"column":4},"end":{"line":89,"column":5}},"type":"if","locations":[{"start":{"line":87,"column":4},"end":{"line":89,"column":5}},{"start":{},"end":{}}],"line":87},"10":{"loc":{"start":{"line":120,"column":4},"end":{"line":122,"column":5}},"type":"if","locations":[{"start":{"line":120,"column":4},"end":{"line":122,"column":5}},{"start":{},"end":{}}],"line":120},"11":{"loc":{"start":{"line":146,"column":4},"end":{"line":148,"column":5}},"type":"if","locations":[{"start":{"line":146,"column":4},"end":{"line":148,"column":5}},{"start":{},"end":{}}],"line":146},"12":{"loc":{"start":{"line":169,"column":4},"end":{"line":171,"column":5}},"type":"if","locations":[{"start":{"line":169,"column":4},"end":{"line":171,"column":5}},{"start":{},"end":{}}],"line":169},"13":{"loc":{"start":{"line":204,"column":4},"end":{"line":206,"column":5}},"type":"if","locations":[{"start":{"line":204,"column":4},"end":{"line":206,"column":5}},{"start":{},"end":{}}],"line":204},"14":{"loc":{"start":{"line":228,"column":4},"end":{"line":230,"column":5}},"type":"if","locations":[{"start":{"line":228,"column":4},"end":{"line":230,"column":5}},{"start":{},"end":{}}],"line":228},"15":{"loc":{"start":{"line":233,"column":4},"end":{"line":235,"column":5}},"type":"if","locations":[{"start":{"line":233,"column":4},"end":{"line":235,"column":5}},{"start":{},"end":{}}],"line":233},"16":{"loc":{"start":{"line":233,"column":8},"end":{"line":233,"column":84}},"type":"binary-expr","locations":[{"start":{"line":233,"column":8},"end":{"line":233,"column":55}},{"start":{"line":233,"column":59},"end":{"line":233,"column":84}}],"line":233},"17":{"loc":{"start":{"line":241,"column":4},"end":{"line":245,"column":5}},"type":"if","locations":[{"start":{"line":241,"column":4},"end":{"line":245,"column":5}},{"start":{},"end":{}}],"line":241},"18":{"loc":{"start":{"line":246,"column":4},"end":{"line":250,"column":5}},"type":"if","locations":[{"start":{"line":246,"column":4},"end":{"line":250,"column":5}},{"start":{},"end":{}}],"line":246},"19":{"loc":{"start":{"line":251,"column":4},"end":{"line":255,"column":5}},"type":"if","locations":[{"start":{"line":251,"column":4},"end":{"line":255,"column":5}},{"start":{},"end":{}}],"line":251},"20":{"loc":{"start":{"line":256,"column":4},"end":{"line":260,"column":5}},"type":"if","locations":[{"start":{"line":256,"column":4},"end":{"line":260,"column":5}},{"start":{},"end":{}}],"line":256},"21":{"loc":{"start":{"line":261,"column":4},"end":{"line":265,"column":5}},"type":"if","locations":[{"start":{"line":261,"column":4},"end":{"line":265,"column":5}},{"start":{},"end":{}}],"line":261},"22":{"loc":{"start":{"line":266,"column":4},"end":{"line":270,"column":5}},"type":"if","locations":[{"start":{"line":266,"column":4},"end":{"line":270,"column":5}},{"start":{},"end":{}}],"line":266},"23":{"loc":{"start":{"line":271,"column":4},"end":{"line":275,"column":5}},"type":"if","locations":[{"start":{"line":271,"column":4},"end":{"line":275,"column":5}},{"start":{},"end":{}}],"line":271},"24":{"loc":{"start":{"line":276,"column":4},"end":{"line":280,"column":5}},"type":"if","locations":[{"start":{"line":276,"column":4},"end":{"line":280,"column":5}},{"start":{},"end":{}}],"line":276},"25":{"loc":{"start":{"line":281,"column":4},"end":{"line":285,"column":5}},"type":"if","locations":[{"start":{"line":281,"column":4},"end":{"line":285,"column":5}},{"start":{},"end":{}}],"line":281},"26":{"loc":{"start":{"line":286,"column":4},"end":{"line":290,"column":5}},"type":"if","locations":[{"start":{"line":286,"column":4},"end":{"line":290,"column":5}},{"start":{},"end":{}}],"line":286},"27":{"loc":{"start":{"line":292,"column":4},"end":{"line":294,"column":5}},"type":"if","locations":[{"start":{"line":292,"column":4},"end":{"line":294,"column":5}},{"start":{},"end":{}}],"line":292},"28":{"loc":{"start":{"line":315,"column":20},"end":{"line":315,"column":28}},"type":"default-arg","locations":[{"start":{"line":315,"column":27},"end":{"line":315,"column":28}}],"line":315},"29":{"loc":{"start":{"line":315,"column":30},"end":{"line":315,"column":40}},"type":"default-arg","locations":[{"start":{"line":315,"column":38},"end":{"line":315,"column":40}}],"line":315},"30":{"loc":{"start":{"line":327,"column":4},"end":{"line":331,"column":5}},"type":"if","locations":[{"start":{"line":327,"column":4},"end":{"line":331,"column":5}},{"start":{},"end":{}}],"line":327},"31":{"loc":{"start":{"line":353,"column":4},"end":{"line":356,"column":5}},"type":"if","locations":[{"start":{"line":353,"column":4},"end":{"line":356,"column":5}},{"start":{},"end":{}}],"line":353}},"s":{"0":7,"1":7,"2":7,"3":7,"4":7,"5":7,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1,"19":0,"20":0,"21":0,"22":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":1,"32":1,"33":1,"34":1,"35":1,"36":1,"37":1,"38":1,"39":1,"40":1,"41":1,"42":1,"43":1,"44":1,"45":1,"46":1,"47":1,"48":0,"49":0,"50":0,"51":1,"52":1,"53":1,"54":1,"55":1,"56":1,"57":1,"58":1,"59":0,"60":0,"61":7,"62":1,"63":1,"64":1,"65":1,"66":0,"67":1,"68":0,"69":0,"70":7,"71":3,"72":3,"73":3,"74":0,"75":3,"76":3,"77":3,"78":0,"79":3,"80":3,"81":0,"82":0,"83":7,"84":1,"85":1,"86":1,"87":0,"88":1,"89":1,"90":1,"91":1,"92":0,"93":1,"94":0,"95":1,"96":1,"97":1,"98":1,"99":0,"100":0,"101":0,"102":1,"103":0,"104":0,"105":0,"106":1,"107":0,"108":0,"109":0,"110":1,"111":0,"112":0,"113":0,"114":1,"115":0,"116":0,"117":0,"118":1,"119":0,"120":0,"121":0,"122":1,"123":0,"124":0,"125":0,"126":1,"127":0,"128":0,"129":0,"130":1,"131":1,"132":1,"133":1,"134":1,"135":0,"136":0,"137":0,"138":1,"139":0,"140":1,"141":1,"142":1,"143":1,"144":0,"145":0,"146":7,"147":0,"148":0,"149":0,"150":0,"151":0,"152":0,"153":0,"154":0,"155":0,"156":0,"157":0,"158":0,"159":0,"160":0,"161":0,"162":0,"163":0,"164":0,"165":0,"166":0,"167":0,"168":0,"169":0,"170":0,"171":0,"172":0,"173":0,"174":0,"175":7},"f":{"0":1,"1":1,"2":1,"3":1,"4":3,"5":1,"6":0},"b":{"0":[1],"1":[1],"2":[1,0],"3":[0,1],"4":[1,0],"5":[1,0],"6":[1,0],"7":[0,1],"8":[1,0],"9":[1,0],"10":[0,1],"11":[0,3],"12":[0,3],"13":[0,1],"14":[0,1],"15":[0,1],"16":[1,0],"17":[0,1],"18":[0,1],"19":[0,1],"20":[0,1],"21":[0,1],"22":[0,1],"23":[0,1],"24":[0,1],"25":[1,0],"26":[0,1],"27":[0,1],"28":[0],"29":[0],"30":[0,0],"31":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"f1c8da8257855c2c7ec665514100e4b8fbf994fc"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/routes/employers.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/routes/employers.js","statementMap":{"0":{"start":{"line":1,"column":16},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":35},"end":{"line":2,"column":63}},"2":{"start":{"line":3,"column":13},"end":{"line":3,"column":46}},"3":{"start":{"line":4,"column":43},"end":{"line":4,"column":72}},"4":{"start":{"line":6,"column":15},"end":{"line":6,"column":31}},"5":{"start":{"line":9,"column":0},"end":{"line":22,"column":3}},"6":{"start":{"line":10,"column":2},"end":{"line":21,"column":3}},"7":{"start":{"line":11,"column":19},"end":{"line":16,"column":6}},"8":{"start":{"line":17,"column":4},"end":{"line":17,"column":26}},"9":{"start":{"line":19,"column":4},"end":{"line":19,"column":49}},"10":{"start":{"line":20,"column":4},"end":{"line":20,"column":65}},"11":{"start":{"line":25,"column":0},"end":{"line":45,"column":3}},"12":{"start":{"line":26,"column":2},"end":{"line":44,"column":3}},"13":{"start":{"line":27,"column":19},"end":{"line":27,"column":29}},"14":{"start":{"line":29,"column":19},"end":{"line":34,"column":12}},"15":{"start":{"line":36,"column":4},"end":{"line":38,"column":5}},"16":{"start":{"line":37,"column":6},"end":{"line":37,"column":67}},"17":{"start":{"line":40,"column":4},"end":{"line":40,"column":29}},"18":{"start":{"line":42,"column":4},"end":{"line":42,"column":48}},"19":{"start":{"line":43,"column":4},"end":{"line":43,"column":64}},"20":{"start":{"line":48,"column":0},"end":{"line":97,"column":3}},"21":{"start":{"line":57,"column":2},"end":{"line":96,"column":3}},"22":{"start":{"line":58,"column":19},"end":{"line":58,"column":40}},"23":{"start":{"line":59,"column":4},"end":{"line":61,"column":5}},"24":{"start":{"line":60,"column":6},"end":{"line":60,"column":62}},"25":{"start":{"line":71,"column":8},"end":{"line":71,"column":16}},"26":{"start":{"line":74,"column":29},"end":{"line":77,"column":5}},"27":{"start":{"line":79,"column":4},"end":{"line":81,"column":5}},"28":{"start":{"line":80,"column":6},"end":{"line":80,"column":80}},"29":{"start":{"line":83,"column":19},"end":{"line":87,"column":95}},"30":{"start":{"line":89,"column":4},"end":{"line":92,"column":7}},"31":{"start":{"line":94,"column":4},"end":{"line":94,"column":51}},"32":{"start":{"line":95,"column":4},"end":{"line":95,"column":73}},"33":{"start":{"line":100,"column":0},"end":{"line":198,"column":3}},"34":{"start":{"line":109,"column":2},"end":{"line":197,"column":3}},"35":{"start":{"line":110,"column":19},"end":{"line":110,"column":40}},"36":{"start":{"line":111,"column":4},"end":{"line":113,"column":5}},"37":{"start":{"line":112,"column":6},"end":{"line":112,"column":62}},"38":{"start":{"line":115,"column":19},"end":{"line":115,"column":29}},"39":{"start":{"line":124,"column":8},"end":{"line":124,"column":16}},"40":{"start":{"line":127,"column":27},"end":{"line":130,"column":5}},"41":{"start":{"line":132,"column":4},"end":{"line":134,"column":5}},"42":{"start":{"line":133,"column":6},"end":{"line":133,"column":67}},"43":{"start":{"line":137,"column":4},"end":{"line":139,"column":5}},"44":{"start":{"line":138,"column":6},"end":{"line":138,"column":62}},"45":{"start":{"line":141,"column":25},"end":{"line":141,"column":27}},"46":{"start":{"line":142,"column":25},"end":{"line":142,"column":27}},"47":{"start":{"line":143,"column":21},"end":{"line":143,"column":22}},"48":{"start":{"line":145,"column":4},"end":{"line":149,"column":5}},"49":{"start":{"line":146,"column":6},"end":{"line":146,"column":57}},"50":{"start":{"line":147,"column":6},"end":{"line":147,"column":37}},"51":{"start":{"line":148,"column":6},"end":{"line":148,"column":19}},"52":{"start":{"line":150,"column":4},"end":{"line":154,"column":5}},"53":{"start":{"line":151,"column":6},"end":{"line":151,"column":53}},"54":{"start":{"line":152,"column":6},"end":{"line":152,"column":34}},"55":{"start":{"line":153,"column":6},"end":{"line":153,"column":19}},"56":{"start":{"line":155,"column":4},"end":{"line":159,"column":5}},"57":{"start":{"line":156,"column":6},"end":{"line":156,"column":57}},"58":{"start":{"line":157,"column":6},"end":{"line":157,"column":37}},"59":{"start":{"line":158,"column":6},"end":{"line":158,"column":19}},"60":{"start":{"line":160,"column":4},"end":{"line":164,"column":5}},"61":{"start":{"line":161,"column":6},"end":{"line":161,"column":52}},"62":{"start":{"line":162,"column":6},"end":{"line":162,"column":33}},"63":{"start":{"line":163,"column":6},"end":{"line":163,"column":19}},"64":{"start":{"line":165,"column":4},"end":{"line":169,"column":5}},"65":{"start":{"line":166,"column":6},"end":{"line":166,"column":56}},"66":{"start":{"line":167,"column":6},"end":{"line":167,"column":37}},"67":{"start":{"line":168,"column":6},"end":{"line":168,"column":19}},"68":{"start":{"line":170,"column":4},"end":{"line":174,"column":5}},"69":{"start":{"line":171,"column":6},"end":{"line":171,"column":52}},"70":{"start":{"line":172,"column":6},"end":{"line":172,"column":33}},"71":{"start":{"line":173,"column":6},"end":{"line":173,"column":19}},"72":{"start":{"line":175,"column":4},"end":{"line":179,"column":5}},"73":{"start":{"line":176,"column":6},"end":{"line":176,"column":50}},"74":{"start":{"line":177,"column":6},"end":{"line":177,"column":31}},"75":{"start":{"line":178,"column":6},"end":{"line":178,"column":19}},"76":{"start":{"line":181,"column":4},"end":{"line":183,"column":5}},"77":{"start":{"line":182,"column":6},"end":{"line":182,"column":68}},"78":{"start":{"line":185,"column":4},"end":{"line":185,"column":26}},"79":{"start":{"line":186,"column":18},"end":{"line":186,"column":137}},"80":{"start":{"line":188,"column":19},"end":{"line":188,"column":56}},"81":{"start":{"line":190,"column":4},"end":{"line":193,"column":7}},"82":{"start":{"line":195,"column":4},"end":{"line":195,"column":51}},"83":{"start":{"line":196,"column":4},"end":{"line":196,"column":73}},"84":{"start":{"line":201,"column":0},"end":{"line":254,"column":3}},"85":{"start":{"line":202,"column":2},"end":{"line":253,"column":3}},"86":{"start":{"line":203,"column":19},"end":{"line":203,"column":29}},"87":{"start":{"line":204,"column":45},"end":{"line":204,"column":54}},"88":{"start":{"line":206,"column":16},"end":{"line":209,"column":5}},"89":{"start":{"line":210,"column":24},"end":{"line":210,"column":28}},"90":{"start":{"line":211,"column":21},"end":{"line":211,"column":22}},"91":{"start":{"line":213,"column":4},"end":{"line":217,"column":5}},"92":{"start":{"line":214,"column":6},"end":{"line":214,"column":19}},"93":{"start":{"line":215,"column":6},"end":{"line":215,"column":46}},"94":{"start":{"line":216,"column":6},"end":{"line":216,"column":31}},"95":{"start":{"line":219,"column":4},"end":{"line":219,"column":41}},"96":{"start":{"line":222,"column":19},"end":{"line":222,"column":37}},"97":{"start":{"line":223,"column":4},"end":{"line":223,"column":17}},"98":{"start":{"line":224,"column":4},"end":{"line":224,"column":37}},"99":{"start":{"line":225,"column":4},"end":{"line":225,"column":28}},"100":{"start":{"line":226,"column":4},"end":{"line":226,"column":17}},"101":{"start":{"line":227,"column":4},"end":{"line":227,"column":38}},"102":{"start":{"line":228,"column":4},"end":{"line":228,"column":29}},"103":{"start":{"line":230,"column":19},"end":{"line":230,"column":55}},"104":{"start":{"line":233,"column":21},"end":{"line":233,"column":71}},"105":{"start":{"line":234,"column":24},"end":{"line":234,"column":28}},"106":{"start":{"line":235,"column":4},"end":{"line":238,"column":5}},"107":{"start":{"line":236,"column":6},"end":{"line":236,"column":39}},"108":{"start":{"line":237,"column":6},"end":{"line":237,"column":31}},"109":{"start":{"line":239,"column":24},"end":{"line":239,"column":65}},"110":{"start":{"line":241,"column":4},"end":{"line":249,"column":7}},"111":{"start":{"line":251,"column":4},"end":{"line":251,"column":53}},"112":{"start":{"line":252,"column":4},"end":{"line":252,"column":69}},"113":{"start":{"line":256,"column":0},"end":{"line":256,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":9,"column":35},"end":{"line":9,"column":36}},"loc":{"start":{"line":9,"column":55},"end":{"line":22,"column":1}},"line":9},"1":{"name":"(anonymous_1)","decl":{"start":{"line":25,"column":38},"end":{"line":25,"column":39}},"loc":{"start":{"line":25,"column":58},"end":{"line":45,"column":1}},"line":25},"2":{"name":"(anonymous_2)","decl":{"start":{"line":56,"column":3},"end":{"line":56,"column":4}},"loc":{"start":{"line":56,"column":23},"end":{"line":97,"column":1}},"line":56},"3":{"name":"(anonymous_3)","decl":{"start":{"line":108,"column":3},"end":{"line":108,"column":4}},"loc":{"start":{"line":108,"column":23},"end":{"line":198,"column":1}},"line":108},"4":{"name":"(anonymous_4)","decl":{"start":{"line":201,"column":43},"end":{"line":201,"column":44}},"loc":{"start":{"line":201,"column":63},"end":{"line":254,"column":1}},"line":201}},"branchMap":{"0":{"loc":{"start":{"line":36,"column":4},"end":{"line":38,"column":5}},"type":"if","locations":[{"start":{"line":36,"column":4},"end":{"line":38,"column":5}},{"start":{},"end":{}}],"line":36},"1":{"loc":{"start":{"line":59,"column":4},"end":{"line":61,"column":5}},"type":"if","locations":[{"start":{"line":59,"column":4},"end":{"line":61,"column":5}},{"start":{},"end":{}}],"line":59},"2":{"loc":{"start":{"line":79,"column":4},"end":{"line":81,"column":5}},"type":"if","locations":[{"start":{"line":79,"column":4},"end":{"line":81,"column":5}},{"start":{},"end":{}}],"line":79},"3":{"loc":{"start":{"line":111,"column":4},"end":{"line":113,"column":5}},"type":"if","locations":[{"start":{"line":111,"column":4},"end":{"line":113,"column":5}},{"start":{},"end":{}}],"line":111},"4":{"loc":{"start":{"line":132,"column":4},"end":{"line":134,"column":5}},"type":"if","locations":[{"start":{"line":132,"column":4},"end":{"line":134,"column":5}},{"start":{},"end":{}}],"line":132},"5":{"loc":{"start":{"line":137,"column":4},"end":{"line":139,"column":5}},"type":"if","locations":[{"start":{"line":137,"column":4},"end":{"line":139,"column":5}},{"start":{},"end":{}}],"line":137},"6":{"loc":{"start":{"line":137,"column":8},"end":{"line":137,"column":83}},"type":"binary-expr","locations":[{"start":{"line":137,"column":8},"end":{"line":137,"column":54}},{"start":{"line":137,"column":58},"end":{"line":137,"column":83}}],"line":137},"7":{"loc":{"start":{"line":145,"column":4},"end":{"line":149,"column":5}},"type":"if","locations":[{"start":{"line":145,"column":4},"end":{"line":149,"column":5}},{"start":{},"end":{}}],"line":145},"8":{"loc":{"start":{"line":150,"column":4},"end":{"line":154,"column":5}},"type":"if","locations":[{"start":{"line":150,"column":4},"end":{"line":154,"column":5}},{"start":{},"end":{}}],"line":150},"9":{"loc":{"start":{"line":155,"column":4},"end":{"line":159,"column":5}},"type":"if","locations":[{"start":{"line":155,"column":4},"end":{"line":159,"column":5}},{"start":{},"end":{}}],"line":155},"10":{"loc":{"start":{"line":160,"column":4},"end":{"line":164,"column":5}},"type":"if","locations":[{"start":{"line":160,"column":4},"end":{"line":164,"column":5}},{"start":{},"end":{}}],"line":160},"11":{"loc":{"start":{"line":165,"column":4},"end":{"line":169,"column":5}},"type":"if","locations":[{"start":{"line":165,"column":4},"end":{"line":169,"column":5}},{"start":{},"end":{}}],"line":165},"12":{"loc":{"start":{"line":170,"column":4},"end":{"line":174,"column":5}},"type":"if","locations":[{"start":{"line":170,"column":4},"end":{"line":174,"column":5}},{"start":{},"end":{}}],"line":170},"13":{"loc":{"start":{"line":175,"column":4},"end":{"line":179,"column":5}},"type":"if","locations":[{"start":{"line":175,"column":4},"end":{"line":179,"column":5}},{"start":{},"end":{}}],"line":175},"14":{"loc":{"start":{"line":181,"column":4},"end":{"line":183,"column":5}},"type":"if","locations":[{"start":{"line":181,"column":4},"end":{"line":183,"column":5}},{"start":{},"end":{}}],"line":181},"15":{"loc":{"start":{"line":204,"column":20},"end":{"line":204,"column":28}},"type":"default-arg","locations":[{"start":{"line":204,"column":27},"end":{"line":204,"column":28}}],"line":204},"16":{"loc":{"start":{"line":204,"column":30},"end":{"line":204,"column":40}},"type":"default-arg","locations":[{"start":{"line":204,"column":38},"end":{"line":204,"column":40}}],"line":204},"17":{"loc":{"start":{"line":213,"column":4},"end":{"line":217,"column":5}},"type":"if","locations":[{"start":{"line":213,"column":4},"end":{"line":217,"column":5}},{"start":{},"end":{}}],"line":213},"18":{"loc":{"start":{"line":235,"column":4},"end":{"line":238,"column":5}},"type":"if","locations":[{"start":{"line":235,"column":4},"end":{"line":238,"column":5}},{"start":{},"end":{}}],"line":235}},"s":{"0":7,"1":7,"2":7,"3":7,"4":7,"5":7,"6":1,"7":1,"8":1,"9":0,"10":0,"11":7,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":7,"21":11,"22":11,"23":11,"24":0,"25":11,"26":11,"27":11,"28":0,"29":11,"30":11,"31":0,"32":0,"33":7,"34":1,"35":1,"36":1,"37":0,"38":1,"39":1,"40":1,"41":1,"42":0,"43":1,"44":0,"45":1,"46":1,"47":1,"48":1,"49":0,"50":0,"51":0,"52":1,"53":0,"54":0,"55":0,"56":1,"57":0,"58":0,"59":0,"60":1,"61":0,"62":0,"63":0,"64":1,"65":1,"66":1,"67":1,"68":1,"69":0,"70":0,"71":0,"72":1,"73":0,"74":0,"75":0,"76":1,"77":0,"78":1,"79":1,"80":1,"81":1,"82":0,"83":0,"84":7,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":0,"96":0,"97":0,"98":0,"99":0,"100":0,"101":0,"102":0,"103":0,"104":0,"105":0,"106":0,"107":0,"108":0,"109":0,"110":0,"111":0,"112":0,"113":7},"f":{"0":1,"1":0,"2":11,"3":1,"4":0},"b":{"0":[0,0],"1":[0,11],"2":[0,11],"3":[0,1],"4":[0,1],"5":[0,1],"6":[1,0],"7":[0,1],"8":[0,1],"9":[0,1],"10":[0,1],"11":[1,0],"12":[0,1],"13":[0,1],"14":[0,1],"15":[0],"16":[0],"17":[0,0],"18":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"31c3d3dcbd76c5440d9fc7473ad52bf8f1de38f7"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/routes/jobs.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/routes/jobs.js","statementMap":{"0":{"start":{"line":1,"column":16},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":35},"end":{"line":2,"column":63}},"2":{"start":{"line":3,"column":13},"end":{"line":3,"column":46}},"3":{"start":{"line":4,"column":43},"end":{"line":4,"column":72}},"4":{"start":{"line":6,"column":15},"end":{"line":6,"column":31}},"5":{"start":{"line":9,"column":0},"end":{"line":183,"column":3}},"6":{"start":{"line":10,"column":2},"end":{"line":182,"column":3}},"7":{"start":{"line":23,"column":8},"end":{"line":23,"column":17}},"8":{"start":{"line":25,"column":16},"end":{"line":29,"column":5}},"9":{"start":{"line":30,"column":24},"end":{"line":30,"column":26}},"10":{"start":{"line":31,"column":21},"end":{"line":31,"column":22}},"11":{"start":{"line":32,"column":23},"end":{"line":32,"column":25}},"12":{"start":{"line":35,"column":4},"end":{"line":35,"column":17}},"13":{"start":{"line":36,"column":4},"end":{"line":36,"column":49}},"14":{"start":{"line":37,"column":4},"end":{"line":37,"column":29}},"15":{"start":{"line":39,"column":4},"end":{"line":43,"column":5}},"16":{"start":{"line":40,"column":6},"end":{"line":40,"column":19}},"17":{"start":{"line":41,"column":6},"end":{"line":41,"column":94}},"18":{"start":{"line":42,"column":6},"end":{"line":42,"column":38}},"19":{"start":{"line":45,"column":4},"end":{"line":49,"column":5}},"20":{"start":{"line":46,"column":6},"end":{"line":46,"column":19}},"21":{"start":{"line":47,"column":6},"end":{"line":47,"column":57}},"22":{"start":{"line":48,"column":6},"end":{"line":48,"column":40}},"23":{"start":{"line":51,"column":4},"end":{"line":55,"column":5}},"24":{"start":{"line":52,"column":6},"end":{"line":52,"column":19}},"25":{"start":{"line":53,"column":6},"end":{"line":53,"column":60}},"26":{"start":{"line":54,"column":6},"end":{"line":54,"column":39}},"27":{"start":{"line":57,"column":4},"end":{"line":61,"column":5}},"28":{"start":{"line":58,"column":6},"end":{"line":58,"column":19}},"29":{"start":{"line":59,"column":6},"end":{"line":59,"column":61}},"30":{"start":{"line":60,"column":6},"end":{"line":60,"column":40}},"31":{"start":{"line":63,"column":4},"end":{"line":68,"column":5}},"32":{"start":{"line":64,"column":25},"end":{"line":64,"column":61}},"33":{"start":{"line":64,"column":52},"end":{"line":64,"column":60}},"34":{"start":{"line":65,"column":6},"end":{"line":65,"column":19}},"35":{"start":{"line":66,"column":6},"end":{"line":66,"column":61}},"36":{"start":{"line":67,"column":6},"end":{"line":67,"column":35}},"37":{"start":{"line":70,"column":4},"end":{"line":74,"column":5}},"38":{"start":{"line":71,"column":6},"end":{"line":71,"column":19}},"39":{"start":{"line":72,"column":6},"end":{"line":72,"column":56}},"40":{"start":{"line":73,"column":6},"end":{"line":73,"column":44}},"41":{"start":{"line":76,"column":4},"end":{"line":80,"column":5}},"42":{"start":{"line":77,"column":6},"end":{"line":77,"column":19}},"43":{"start":{"line":78,"column":6},"end":{"line":78,"column":56}},"44":{"start":{"line":79,"column":6},"end":{"line":79,"column":44}},"45":{"start":{"line":82,"column":4},"end":{"line":84,"column":5}},"46":{"start":{"line":83,"column":6},"end":{"line":83,"column":49}},"47":{"start":{"line":86,"column":4},"end":{"line":88,"column":5}},"48":{"start":{"line":87,"column":6},"end":{"line":87,"column":52}},"49":{"start":{"line":90,"column":4},"end":{"line":90,"column":43}},"50":{"start":{"line":93,"column":19},"end":{"line":93,"column":37}},"51":{"start":{"line":94,"column":4},"end":{"line":94,"column":17}},"52":{"start":{"line":95,"column":4},"end":{"line":95,"column":37}},"53":{"start":{"line":96,"column":4},"end":{"line":96,"column":28}},"54":{"start":{"line":97,"column":4},"end":{"line":97,"column":17}},"55":{"start":{"line":98,"column":4},"end":{"line":98,"column":38}},"56":{"start":{"line":99,"column":4},"end":{"line":99,"column":29}},"57":{"start":{"line":101,"column":19},"end":{"line":101,"column":55}},"58":{"start":{"line":104,"column":21},"end":{"line":108,"column":5}},"59":{"start":{"line":109,"column":24},"end":{"line":109,"column":26}},"60":{"start":{"line":110,"column":26},"end":{"line":110,"column":27}},"61":{"start":{"line":111,"column":28},"end":{"line":111,"column":30}},"62":{"start":{"line":113,"column":4},"end":{"line":113,"column":22}},"63":{"start":{"line":114,"column":4},"end":{"line":114,"column":59}},"64":{"start":{"line":115,"column":4},"end":{"line":115,"column":29}},"65":{"start":{"line":117,"column":4},"end":{"line":121,"column":5}},"66":{"start":{"line":118,"column":6},"end":{"line":118,"column":24}},"67":{"start":{"line":119,"column":6},"end":{"line":119,"column":109}},"68":{"start":{"line":120,"column":6},"end":{"line":120,"column":38}},"69":{"start":{"line":123,"column":4},"end":{"line":127,"column":5}},"70":{"start":{"line":124,"column":6},"end":{"line":124,"column":24}},"71":{"start":{"line":125,"column":6},"end":{"line":125,"column":67}},"72":{"start":{"line":126,"column":6},"end":{"line":126,"column":40}},"73":{"start":{"line":129,"column":4},"end":{"line":133,"column":5}},"74":{"start":{"line":130,"column":6},"end":{"line":130,"column":24}},"75":{"start":{"line":131,"column":6},"end":{"line":131,"column":70}},"76":{"start":{"line":132,"column":6},"end":{"line":132,"column":39}},"77":{"start":{"line":135,"column":4},"end":{"line":139,"column":5}},"78":{"start":{"line":136,"column":6},"end":{"line":136,"column":24}},"79":{"start":{"line":137,"column":6},"end":{"line":137,"column":71}},"80":{"start":{"line":138,"column":6},"end":{"line":138,"column":40}},"81":{"start":{"line":141,"column":4},"end":{"line":146,"column":5}},"82":{"start":{"line":142,"column":25},"end":{"line":142,"column":61}},"83":{"start":{"line":142,"column":52},"end":{"line":142,"column":60}},"84":{"start":{"line":143,"column":6},"end":{"line":143,"column":24}},"85":{"start":{"line":144,"column":6},"end":{"line":144,"column":71}},"86":{"start":{"line":145,"column":6},"end":{"line":145,"column":35}},"87":{"start":{"line":148,"column":4},"end":{"line":152,"column":5}},"88":{"start":{"line":149,"column":6},"end":{"line":149,"column":24}},"89":{"start":{"line":150,"column":6},"end":{"line":150,"column":66}},"90":{"start":{"line":151,"column":6},"end":{"line":151,"column":44}},"91":{"start":{"line":154,"column":4},"end":{"line":158,"column":5}},"92":{"start":{"line":155,"column":6},"end":{"line":155,"column":24}},"93":{"start":{"line":156,"column":6},"end":{"line":156,"column":66}},"94":{"start":{"line":157,"column":6},"end":{"line":157,"column":44}},"95":{"start":{"line":160,"column":4},"end":{"line":162,"column":5}},"96":{"start":{"line":161,"column":6},"end":{"line":161,"column":54}},"97":{"start":{"line":164,"column":4},"end":{"line":166,"column":5}},"98":{"start":{"line":165,"column":6},"end":{"line":165,"column":62}},"99":{"start":{"line":168,"column":24},"end":{"line":168,"column":65}},"100":{"start":{"line":170,"column":4},"end":{"line":178,"column":7}},"101":{"start":{"line":180,"column":4},"end":{"line":180,"column":44}},"102":{"start":{"line":181,"column":4},"end":{"line":181,"column":60}},"103":{"start":{"line":186,"column":0},"end":{"line":206,"column":3}},"104":{"start":{"line":187,"column":2},"end":{"line":205,"column":3}},"105":{"start":{"line":188,"column":19},"end":{"line":188,"column":29}},"106":{"start":{"line":190,"column":19},"end":{"line":195,"column":12}},"107":{"start":{"line":197,"column":4},"end":{"line":199,"column":5}},"108":{"start":{"line":198,"column":6},"end":{"line":198,"column":62}},"109":{"start":{"line":201,"column":4},"end":{"line":201,"column":29}},"110":{"start":{"line":203,"column":4},"end":{"line":203,"column":43}},"111":{"start":{"line":204,"column":4},"end":{"line":204,"column":59}},"112":{"start":{"line":209,"column":0},"end":{"line":282,"column":3}},"113":{"start":{"line":225,"column":2},"end":{"line":281,"column":3}},"114":{"start":{"line":226,"column":19},"end":{"line":226,"column":40}},"115":{"start":{"line":227,"column":4},"end":{"line":229,"column":5}},"116":{"start":{"line":228,"column":6},"end":{"line":228,"column":62}},"117":{"start":{"line":246,"column":8},"end":{"line":246,"column":16}},"118":{"start":{"line":250,"column":4},"end":{"line":266,"column":5}},"119":{"start":{"line":251,"column":29},"end":{"line":254,"column":7}},"120":{"start":{"line":255,"column":6},"end":{"line":257,"column":7}},"121":{"start":{"line":256,"column":8},"end":{"line":256,"column":77}},"122":{"start":{"line":258,"column":6},"end":{"line":258,"column":45}},"123":{"start":{"line":261,"column":49},"end":{"line":261,"column":57}},"124":{"start":{"line":262,"column":6},"end":{"line":264,"column":7}},"125":{"start":{"line":263,"column":8},"end":{"line":263,"column":86}},"126":{"start":{"line":265,"column":6},"end":{"line":265,"column":38}},"127":{"start":{"line":268,"column":19},"end":{"line":272,"column":209}},"128":{"start":{"line":274,"column":4},"end":{"line":277,"column":7}},"129":{"start":{"line":279,"column":4},"end":{"line":279,"column":46}},"130":{"start":{"line":280,"column":4},"end":{"line":280,"column":68}},"131":{"start":{"line":285,"column":0},"end":{"line":443,"column":3}},"132":{"start":{"line":302,"column":2},"end":{"line":442,"column":3}},"133":{"start":{"line":303,"column":19},"end":{"line":303,"column":40}},"134":{"start":{"line":304,"column":4},"end":{"line":306,"column":5}},"135":{"start":{"line":305,"column":6},"end":{"line":305,"column":62}},"136":{"start":{"line":308,"column":19},"end":{"line":308,"column":29}},"137":{"start":{"line":325,"column":8},"end":{"line":325,"column":16}},"138":{"start":{"line":328,"column":22},"end":{"line":333,"column":12}},"139":{"start":{"line":335,"column":4},"end":{"line":337,"column":5}},"140":{"start":{"line":336,"column":6},"end":{"line":336,"column":62}},"141":{"start":{"line":339,"column":16},"end":{"line":339,"column":33}},"142":{"start":{"line":342,"column":4},"end":{"line":344,"column":5}},"143":{"start":{"line":343,"column":6},"end":{"line":343,"column":62}},"144":{"start":{"line":346,"column":25},"end":{"line":346,"column":27}},"145":{"start":{"line":347,"column":25},"end":{"line":347,"column":27}},"146":{"start":{"line":348,"column":21},"end":{"line":348,"column":22}},"147":{"start":{"line":350,"column":4},"end":{"line":354,"column":5}},"148":{"start":{"line":351,"column":6},"end":{"line":351,"column":50}},"149":{"start":{"line":352,"column":6},"end":{"line":352,"column":31}},"150":{"start":{"line":353,"column":6},"end":{"line":353,"column":19}},"151":{"start":{"line":355,"column":4},"end":{"line":359,"column":5}},"152":{"start":{"line":356,"column":6},"end":{"line":356,"column":56}},"153":{"start":{"line":357,"column":6},"end":{"line":357,"column":37}},"154":{"start":{"line":358,"column":6},"end":{"line":358,"column":19}},"155":{"start":{"line":360,"column":4},"end":{"line":364,"column":5}},"156":{"start":{"line":361,"column":6},"end":{"line":361,"column":57}},"157":{"start":{"line":362,"column":6},"end":{"line":362,"column":38}},"158":{"start":{"line":363,"column":6},"end":{"line":363,"column":19}},"159":{"start":{"line":365,"column":4},"end":{"line":369,"column":5}},"160":{"start":{"line":366,"column":6},"end":{"line":366,"column":61}},"161":{"start":{"line":367,"column":6},"end":{"line":367,"column":42}},"162":{"start":{"line":368,"column":6},"end":{"line":368,"column":19}},"163":{"start":{"line":370,"column":4},"end":{"line":374,"column":5}},"164":{"start":{"line":371,"column":6},"end":{"line":371,"column":53}},"165":{"start":{"line":372,"column":6},"end":{"line":372,"column":34}},"166":{"start":{"line":373,"column":6},"end":{"line":373,"column":19}},"167":{"start":{"line":375,"column":4},"end":{"line":379,"column":5}},"168":{"start":{"line":376,"column":6},"end":{"line":376,"column":60}},"169":{"start":{"line":377,"column":6},"end":{"line":377,"column":40}},"170":{"start":{"line":378,"column":6},"end":{"line":378,"column":19}},"171":{"start":{"line":380,"column":4},"end":{"line":384,"column":5}},"172":{"start":{"line":381,"column":6},"end":{"line":381,"column":55}},"173":{"start":{"line":382,"column":6},"end":{"line":382,"column":35}},"174":{"start":{"line":383,"column":6},"end":{"line":383,"column":19}},"175":{"start":{"line":385,"column":4},"end":{"line":389,"column":5}},"176":{"start":{"line":386,"column":6},"end":{"line":386,"column":55}},"177":{"start":{"line":387,"column":6},"end":{"line":387,"column":35}},"178":{"start":{"line":388,"column":6},"end":{"line":388,"column":19}},"179":{"start":{"line":390,"column":4},"end":{"line":394,"column":5}},"180":{"start":{"line":391,"column":6},"end":{"line":391,"column":53}},"181":{"start":{"line":392,"column":6},"end":{"line":392,"column":34}},"182":{"start":{"line":393,"column":6},"end":{"line":393,"column":19}},"183":{"start":{"line":395,"column":4},"end":{"line":399,"column":5}},"184":{"start":{"line":396,"column":6},"end":{"line":396,"column":51}},"185":{"start":{"line":397,"column":6},"end":{"line":397,"column":32}},"186":{"start":{"line":398,"column":6},"end":{"line":398,"column":19}},"187":{"start":{"line":400,"column":4},"end":{"line":404,"column":5}},"188":{"start":{"line":401,"column":6},"end":{"line":401,"column":59}},"189":{"start":{"line":402,"column":6},"end":{"line":402,"column":39}},"190":{"start":{"line":403,"column":6},"end":{"line":403,"column":19}},"191":{"start":{"line":405,"column":4},"end":{"line":409,"column":5}},"192":{"start":{"line":406,"column":6},"end":{"line":406,"column":61}},"193":{"start":{"line":407,"column":6},"end":{"line":407,"column":41}},"194":{"start":{"line":408,"column":6},"end":{"line":408,"column":19}},"195":{"start":{"line":410,"column":4},"end":{"line":414,"column":5}},"196":{"start":{"line":411,"column":6},"end":{"line":411,"column":60}},"197":{"start":{"line":412,"column":6},"end":{"line":412,"column":40}},"198":{"start":{"line":413,"column":6},"end":{"line":413,"column":19}},"199":{"start":{"line":415,"column":4},"end":{"line":419,"column":5}},"200":{"start":{"line":416,"column":6},"end":{"line":416,"column":53}},"201":{"start":{"line":417,"column":6},"end":{"line":417,"column":34}},"202":{"start":{"line":418,"column":6},"end":{"line":418,"column":19}},"203":{"start":{"line":420,"column":4},"end":{"line":424,"column":5}},"204":{"start":{"line":421,"column":6},"end":{"line":421,"column":65}},"205":{"start":{"line":422,"column":6},"end":{"line":422,"column":45}},"206":{"start":{"line":423,"column":6},"end":{"line":423,"column":19}},"207":{"start":{"line":426,"column":4},"end":{"line":428,"column":5}},"208":{"start":{"line":427,"column":6},"end":{"line":427,"column":68}},"209":{"start":{"line":430,"column":4},"end":{"line":430,"column":26}},"210":{"start":{"line":431,"column":18},"end":{"line":431,"column":132}},"211":{"start":{"line":433,"column":19},"end":{"line":433,"column":56}},"212":{"start":{"line":435,"column":4},"end":{"line":438,"column":7}},"213":{"start":{"line":440,"column":4},"end":{"line":440,"column":46}},"214":{"start":{"line":441,"column":4},"end":{"line":441,"column":68}},"215":{"start":{"line":446,"column":0},"end":{"line":476,"column":3}},"216":{"start":{"line":447,"column":2},"end":{"line":475,"column":3}},"217":{"start":{"line":448,"column":19},"end":{"line":448,"column":29}},"218":{"start":{"line":451,"column":22},"end":{"line":456,"column":12}},"219":{"start":{"line":458,"column":4},"end":{"line":460,"column":5}},"220":{"start":{"line":459,"column":6},"end":{"line":459,"column":62}},"221":{"start":{"line":462,"column":16},"end":{"line":462,"column":33}},"222":{"start":{"line":465,"column":4},"end":{"line":467,"column":5}},"223":{"start":{"line":466,"column":6},"end":{"line":466,"column":62}},"224":{"start":{"line":469,"column":4},"end":{"line":469,"column":61}},"225":{"start":{"line":471,"column":4},"end":{"line":471,"column":62}},"226":{"start":{"line":473,"column":4},"end":{"line":473,"column":46}},"227":{"start":{"line":474,"column":4},"end":{"line":474,"column":68}},"228":{"start":{"line":478,"column":0},"end":{"line":478,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":9,"column":35},"end":{"line":9,"column":36}},"loc":{"start":{"line":9,"column":55},"end":{"line":183,"column":1}},"line":9},"1":{"name":"(anonymous_1)","decl":{"start":{"line":64,"column":47},"end":{"line":64,"column":48}},"loc":{"start":{"line":64,"column":52},"end":{"line":64,"column":60}},"line":64},"2":{"name":"(anonymous_2)","decl":{"start":{"line":142,"column":47},"end":{"line":142,"column":48}},"loc":{"start":{"line":142,"column":52},"end":{"line":142,"column":60}},"line":142},"3":{"name":"(anonymous_3)","decl":{"start":{"line":186,"column":38},"end":{"line":186,"column":39}},"loc":{"start":{"line":186,"column":58},"end":{"line":206,"column":1}},"line":186},"4":{"name":"(anonymous_4)","decl":{"start":{"line":224,"column":3},"end":{"line":224,"column":4}},"loc":{"start":{"line":224,"column":23},"end":{"line":282,"column":1}},"line":224},"5":{"name":"(anonymous_5)","decl":{"start":{"line":301,"column":3},"end":{"line":301,"column":4}},"loc":{"start":{"line":301,"column":23},"end":{"line":443,"column":1}},"line":301},"6":{"name":"(anonymous_6)","decl":{"start":{"line":446,"column":41},"end":{"line":446,"column":42}},"loc":{"start":{"line":446,"column":61},"end":{"line":476,"column":1}},"line":446}},"branchMap":{"0":{"loc":{"start":{"line":20,"column":6},"end":{"line":20,"column":23}},"type":"default-arg","locations":[{"start":{"line":20,"column":15},"end":{"line":20,"column":23}}],"line":20},"1":{"loc":{"start":{"line":21,"column":6},"end":{"line":21,"column":14}},"type":"default-arg","locations":[{"start":{"line":21,"column":13},"end":{"line":21,"column":14}}],"line":21},"2":{"loc":{"start":{"line":22,"column":6},"end":{"line":22,"column":16}},"type":"default-arg","locations":[{"start":{"line":22,"column":14},"end":{"line":22,"column":16}}],"line":22},"3":{"loc":{"start":{"line":39,"column":4},"end":{"line":43,"column":5}},"type":"if","locations":[{"start":{"line":39,"column":4},"end":{"line":43,"column":5}},{"start":{},"end":{}}],"line":39},"4":{"loc":{"start":{"line":45,"column":4},"end":{"line":49,"column":5}},"type":"if","locations":[{"start":{"line":45,"column":4},"end":{"line":49,"column":5}},{"start":{},"end":{}}],"line":45},"5":{"loc":{"start":{"line":51,"column":4},"end":{"line":55,"column":5}},"type":"if","locations":[{"start":{"line":51,"column":4},"end":{"line":55,"column":5}},{"start":{},"end":{}}],"line":51},"6":{"loc":{"start":{"line":57,"column":4},"end":{"line":61,"column":5}},"type":"if","locations":[{"start":{"line":57,"column":4},"end":{"line":61,"column":5}},{"start":{},"end":{}}],"line":57},"7":{"loc":{"start":{"line":63,"column":4},"end":{"line":68,"column":5}},"type":"if","locations":[{"start":{"line":63,"column":4},"end":{"line":68,"column":5}},{"start":{},"end":{}}],"line":63},"8":{"loc":{"start":{"line":70,"column":4},"end":{"line":74,"column":5}},"type":"if","locations":[{"start":{"line":70,"column":4},"end":{"line":74,"column":5}},{"start":{},"end":{}}],"line":70},"9":{"loc":{"start":{"line":76,"column":4},"end":{"line":80,"column":5}},"type":"if","locations":[{"start":{"line":76,"column":4},"end":{"line":80,"column":5}},{"start":{},"end":{}}],"line":76},"10":{"loc":{"start":{"line":82,"column":4},"end":{"line":84,"column":5}},"type":"if","locations":[{"start":{"line":82,"column":4},"end":{"line":84,"column":5}},{"start":{},"end":{}}],"line":82},"11":{"loc":{"start":{"line":86,"column":4},"end":{"line":88,"column":5}},"type":"if","locations":[{"start":{"line":86,"column":4},"end":{"line":88,"column":5}},{"start":{},"end":{}}],"line":86},"12":{"loc":{"start":{"line":117,"column":4},"end":{"line":121,"column":5}},"type":"if","locations":[{"start":{"line":117,"column":4},"end":{"line":121,"column":5}},{"start":{},"end":{}}],"line":117},"13":{"loc":{"start":{"line":123,"column":4},"end":{"line":127,"column":5}},"type":"if","locations":[{"start":{"line":123,"column":4},"end":{"line":127,"column":5}},{"start":{},"end":{}}],"line":123},"14":{"loc":{"start":{"line":129,"column":4},"end":{"line":133,"column":5}},"type":"if","locations":[{"start":{"line":129,"column":4},"end":{"line":133,"column":5}},{"start":{},"end":{}}],"line":129},"15":{"loc":{"start":{"line":135,"column":4},"end":{"line":139,"column":5}},"type":"if","locations":[{"start":{"line":135,"column":4},"end":{"line":139,"column":5}},{"start":{},"end":{}}],"line":135},"16":{"loc":{"start":{"line":141,"column":4},"end":{"line":146,"column":5}},"type":"if","locations":[{"start":{"line":141,"column":4},"end":{"line":146,"column":5}},{"start":{},"end":{}}],"line":141},"17":{"loc":{"start":{"line":148,"column":4},"end":{"line":152,"column":5}},"type":"if","locations":[{"start":{"line":148,"column":4},"end":{"line":152,"column":5}},{"start":{},"end":{}}],"line":148},"18":{"loc":{"start":{"line":154,"column":4},"end":{"line":158,"column":5}},"type":"if","locations":[{"start":{"line":154,"column":4},"end":{"line":158,"column":5}},{"start":{},"end":{}}],"line":154},"19":{"loc":{"start":{"line":160,"column":4},"end":{"line":162,"column":5}},"type":"if","locations":[{"start":{"line":160,"column":4},"end":{"line":162,"column":5}},{"start":{},"end":{}}],"line":160},"20":{"loc":{"start":{"line":164,"column":4},"end":{"line":166,"column":5}},"type":"if","locations":[{"start":{"line":164,"column":4},"end":{"line":166,"column":5}},{"start":{},"end":{}}],"line":164},"21":{"loc":{"start":{"line":197,"column":4},"end":{"line":199,"column":5}},"type":"if","locations":[{"start":{"line":197,"column":4},"end":{"line":199,"column":5}},{"start":{},"end":{}}],"line":197},"22":{"loc":{"start":{"line":227,"column":4},"end":{"line":229,"column":5}},"type":"if","locations":[{"start":{"line":227,"column":4},"end":{"line":229,"column":5}},{"start":{},"end":{}}],"line":227},"23":{"loc":{"start":{"line":240,"column":6},"end":{"line":240,"column":22}},"type":"default-arg","locations":[{"start":{"line":240,"column":17},"end":{"line":240,"column":22}}],"line":240},"24":{"loc":{"start":{"line":241,"column":6},"end":{"line":241,"column":27}},"type":"default-arg","locations":[{"start":{"line":241,"column":22},"end":{"line":241,"column":27}}],"line":241},"25":{"loc":{"start":{"line":250,"column":4},"end":{"line":266,"column":5}},"type":"if","locations":[{"start":{"line":250,"column":4},"end":{"line":266,"column":5}},{"start":{"line":259,"column":11},"end":{"line":266,"column":5}}],"line":250},"26":{"loc":{"start":{"line":255,"column":6},"end":{"line":257,"column":7}},"type":"if","locations":[{"start":{"line":255,"column":6},"end":{"line":257,"column":7}},{"start":{},"end":{}}],"line":255},"27":{"loc":{"start":{"line":262,"column":6},"end":{"line":264,"column":7}},"type":"if","locations":[{"start":{"line":262,"column":6},"end":{"line":264,"column":7}},{"start":{},"end":{}}],"line":262},"28":{"loc":{"start":{"line":304,"column":4},"end":{"line":306,"column":5}},"type":"if","locations":[{"start":{"line":304,"column":4},"end":{"line":306,"column":5}},{"start":{},"end":{}}],"line":304},"29":{"loc":{"start":{"line":335,"column":4},"end":{"line":337,"column":5}},"type":"if","locations":[{"start":{"line":335,"column":4},"end":{"line":337,"column":5}},{"start":{},"end":{}}],"line":335},"30":{"loc":{"start":{"line":342,"column":4},"end":{"line":344,"column":5}},"type":"if","locations":[{"start":{"line":342,"column":4},"end":{"line":344,"column":5}},{"start":{},"end":{}}],"line":342},"31":{"loc":{"start":{"line":342,"column":8},"end":{"line":342,"column":73}},"type":"binary-expr","locations":[{"start":{"line":342,"column":8},"end":{"line":342,"column":44}},{"start":{"line":342,"column":48},"end":{"line":342,"column":73}}],"line":342},"32":{"loc":{"start":{"line":350,"column":4},"end":{"line":354,"column":5}},"type":"if","locations":[{"start":{"line":350,"column":4},"end":{"line":354,"column":5}},{"start":{},"end":{}}],"line":350},"33":{"loc":{"start":{"line":355,"column":4},"end":{"line":359,"column":5}},"type":"if","locations":[{"start":{"line":355,"column":4},"end":{"line":359,"column":5}},{"start":{},"end":{}}],"line":355},"34":{"loc":{"start":{"line":360,"column":4},"end":{"line":364,"column":5}},"type":"if","locations":[{"start":{"line":360,"column":4},"end":{"line":364,"column":5}},{"start":{},"end":{}}],"line":360},"35":{"loc":{"start":{"line":365,"column":4},"end":{"line":369,"column":5}},"type":"if","locations":[{"start":{"line":365,"column":4},"end":{"line":369,"column":5}},{"start":{},"end":{}}],"line":365},"36":{"loc":{"start":{"line":370,"column":4},"end":{"line":374,"column":5}},"type":"if","locations":[{"start":{"line":370,"column":4},"end":{"line":374,"column":5}},{"start":{},"end":{}}],"line":370},"37":{"loc":{"start":{"line":375,"column":4},"end":{"line":379,"column":5}},"type":"if","locations":[{"start":{"line":375,"column":4},"end":{"line":379,"column":5}},{"start":{},"end":{}}],"line":375},"38":{"loc":{"start":{"line":380,"column":4},"end":{"line":384,"column":5}},"type":"if","locations":[{"start":{"line":380,"column":4},"end":{"line":384,"column":5}},{"start":{},"end":{}}],"line":380},"39":{"loc":{"start":{"line":385,"column":4},"end":{"line":389,"column":5}},"type":"if","locations":[{"start":{"line":385,"column":4},"end":{"line":389,"column":5}},{"start":{},"end":{}}],"line":385},"40":{"loc":{"start":{"line":390,"column":4},"end":{"line":394,"column":5}},"type":"if","locations":[{"start":{"line":390,"column":4},"end":{"line":394,"column":5}},{"start":{},"end":{}}],"line":390},"41":{"loc":{"start":{"line":395,"column":4},"end":{"line":399,"column":5}},"type":"if","locations":[{"start":{"line":395,"column":4},"end":{"line":399,"column":5}},{"start":{},"end":{}}],"line":395},"42":{"loc":{"start":{"line":400,"column":4},"end":{"line":404,"column":5}},"type":"if","locations":[{"start":{"line":400,"column":4},"end":{"line":404,"column":5}},{"start":{},"end":{}}],"line":400},"43":{"loc":{"start":{"line":405,"column":4},"end":{"line":409,"column":5}},"type":"if","locations":[{"start":{"line":405,"column":4},"end":{"line":409,"column":5}},{"start":{},"end":{}}],"line":405},"44":{"loc":{"start":{"line":410,"column":4},"end":{"line":414,"column":5}},"type":"if","locations":[{"start":{"line":410,"column":4},"end":{"line":414,"column":5}},{"start":{},"end":{}}],"line":410},"45":{"loc":{"start":{"line":415,"column":4},"end":{"line":419,"column":5}},"type":"if","locations":[{"start":{"line":415,"column":4},"end":{"line":419,"column":5}},{"start":{},"end":{}}],"line":415},"46":{"loc":{"start":{"line":420,"column":4},"end":{"line":424,"column":5}},"type":"if","locations":[{"start":{"line":420,"column":4},"end":{"line":424,"column":5}},{"start":{},"end":{}}],"line":420},"47":{"loc":{"start":{"line":426,"column":4},"end":{"line":428,"column":5}},"type":"if","locations":[{"start":{"line":426,"column":4},"end":{"line":428,"column":5}},{"start":{},"end":{}}],"line":426},"48":{"loc":{"start":{"line":458,"column":4},"end":{"line":460,"column":5}},"type":"if","locations":[{"start":{"line":458,"column":4},"end":{"line":460,"column":5}},{"start":{},"end":{}}],"line":458},"49":{"loc":{"start":{"line":465,"column":4},"end":{"line":467,"column":5}},"type":"if","locations":[{"start":{"line":465,"column":4},"end":{"line":467,"column":5}},{"start":{},"end":{}}],"line":465},"50":{"loc":{"start":{"line":465,"column":8},"end":{"line":465,"column":73}},"type":"binary-expr","locations":[{"start":{"line":465,"column":8},"end":{"line":465,"column":44}},{"start":{"line":465,"column":48},"end":{"line":465,"column":73}}],"line":465}},"s":{"0":7,"1":7,"2":7,"3":7,"4":7,"5":7,"6":3,"7":3,"8":3,"9":3,"10":3,"11":3,"12":3,"13":3,"14":3,"15":3,"16":1,"17":1,"18":1,"19":3,"20":1,"21":1,"22":1,"23":3,"24":0,"25":0,"26":0,"27":3,"28":0,"29":0,"30":0,"31":3,"32":0,"33":0,"34":0,"35":0,"36":0,"37":3,"38":0,"39":0,"40":0,"41":3,"42":0,"43":0,"44":0,"45":3,"46":0,"47":3,"48":3,"49":3,"50":3,"51":3,"52":3,"53":3,"54":3,"55":3,"56":3,"57":3,"58":3,"59":3,"60":3,"61":3,"62":3,"63":3,"64":3,"65":3,"66":1,"67":1,"68":1,"69":3,"70":1,"71":1,"72":1,"73":3,"74":0,"75":0,"76":0,"77":3,"78":0,"79":0,"80":0,"81":3,"82":0,"83":0,"84":0,"85":0,"86":0,"87":3,"88":0,"89":0,"90":0,"91":3,"92":0,"93":0,"94":0,"95":3,"96":0,"97":3,"98":3,"99":3,"100":3,"101":0,"102":0,"103":7,"104":2,"105":2,"106":2,"107":2,"108":1,"109":1,"110":0,"111":0,"112":7,"113":10,"114":10,"115":10,"116":1,"117":9,"118":9,"119":9,"120":9,"121":0,"122":9,"123":0,"124":0,"125":0,"126":0,"127":9,"128":9,"129":0,"130":0,"131":7,"132":1,"133":1,"134":1,"135":0,"136":1,"137":1,"138":1,"139":1,"140":0,"141":1,"142":1,"143":0,"144":1,"145":1,"146":1,"147":1,"148":1,"149":1,"150":1,"151":1,"152":1,"153":1,"154":1,"155":1,"156":0,"157":0,"158":0,"159":1,"160":0,"161":0,"162":0,"163":1,"164":0,"165":0,"166":0,"167":1,"168":0,"169":0,"170":0,"171":1,"172":0,"173":0,"174":0,"175":1,"176":0,"177":0,"178":0,"179":1,"180":0,"181":0,"182":0,"183":1,"184":0,"185":0,"186":0,"187":1,"188":0,"189":0,"190":0,"191":1,"192":0,"193":0,"194":0,"195":1,"196":0,"197":0,"198":0,"199":1,"200":0,"201":0,"202":0,"203":1,"204":0,"205":0,"206":0,"207":1,"208":0,"209":1,"210":1,"211":1,"212":1,"213":0,"214":0,"215":7,"216":1,"217":1,"218":1,"219":1,"220":0,"221":1,"222":1,"223":0,"224":1,"225":1,"226":0,"227":0,"228":7},"f":{"0":3,"1":0,"2":0,"3":2,"4":10,"5":1,"6":1},"b":{"0":[3],"1":[3],"2":[3],"3":[1,2],"4":[1,2],"5":[0,3],"6":[0,3],"7":[0,3],"8":[0,3],"9":[0,3],"10":[0,3],"11":[3,0],"12":[1,2],"13":[1,2],"14":[0,3],"15":[0,3],"16":[0,3],"17":[0,3],"18":[0,3],"19":[0,3],"20":[3,0],"21":[1,1],"22":[1,9],"23":[9],"24":[7],"25":[9,0],"26":[0,9],"27":[0,0],"28":[0,1],"29":[0,1],"30":[0,1],"31":[1,0],"32":[1,0],"33":[1,0],"34":[0,1],"35":[0,1],"36":[0,1],"37":[0,1],"38":[0,1],"39":[0,1],"40":[0,1],"41":[0,1],"42":[0,1],"43":[0,1],"44":[0,1],"45":[0,1],"46":[0,1],"47":[0,1],"48":[0,1],"49":[0,1],"50":[1,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"57d8fb54d6e57dcbf3dbb4d87bcf8d47f35d5893"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/routes/resumes.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/routes/resumes.js","statementMap":{"0":{"start":{"line":1,"column":16},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":15},"end":{"line":2,"column":32}},"2":{"start":{"line":3,"column":13},"end":{"line":3,"column":28}},"3":{"start":{"line":4,"column":11},"end":{"line":4,"column":24}},"4":{"start":{"line":5,"column":23},"end":{"line":5,"column":38}},"5":{"start":{"line":6,"column":17},"end":{"line":6,"column":45}},"6":{"start":{"line":7,"column":13},"end":{"line":7,"column":46}},"7":{"start":{"line":8,"column":43},"end":{"line":8,"column":72}},"8":{"start":{"line":9,"column":15},"end":{"line":9,"column":35}},"9":{"start":{"line":11,"column":15},"end":{"line":11,"column":31}},"10":{"start":{"line":14,"column":16},"end":{"line":27,"column":2}},"11":{"start":{"line":16,"column":22},"end":{"line":16,"column":72}},"12":{"start":{"line":17,"column":4},"end":{"line":19,"column":5}},"13":{"start":{"line":18,"column":6},"end":{"line":18,"column":51}},"14":{"start":{"line":20,"column":4},"end":{"line":20,"column":24}},"15":{"start":{"line":23,"column":25},"end":{"line":23,"column":52}},"16":{"start":{"line":24,"column":23},"end":{"line":24,"column":64}},"17":{"start":{"line":25,"column":4},"end":{"line":25,"column":25}},"18":{"start":{"line":29,"column":15},"end":{"line":48,"column":2}},"19":{"start":{"line":35,"column":25},"end":{"line":40,"column":5}},"20":{"start":{"line":42,"column":4},"end":{"line":46,"column":5}},"21":{"start":{"line":43,"column":6},"end":{"line":43,"column":21}},"22":{"start":{"line":45,"column":6},"end":{"line":45,"column":90}},"23":{"start":{"line":51,"column":0},"end":{"line":78,"column":3}},"24":{"start":{"line":52,"column":2},"end":{"line":77,"column":3}},"25":{"start":{"line":53,"column":28},"end":{"line":53,"column":38}},"26":{"start":{"line":56,"column":4},"end":{"line":66,"column":5}},"27":{"start":{"line":57,"column":30},"end":{"line":60,"column":7}},"28":{"start":{"line":61,"column":6},"end":{"line":63,"column":7}},"29":{"start":{"line":62,"column":8},"end":{"line":62,"column":64}},"30":{"start":{"line":64,"column":11},"end":{"line":66,"column":5}},"31":{"start":{"line":65,"column":6},"end":{"line":65,"column":62}},"32":{"start":{"line":68,"column":19},"end":{"line":71,"column":5}},"33":{"start":{"line":73,"column":4},"end":{"line":73,"column":26}},"34":{"start":{"line":75,"column":4},"end":{"line":75,"column":47}},"35":{"start":{"line":76,"column":4},"end":{"line":76,"column":63}},"36":{"start":{"line":81,"column":0},"end":{"line":114,"column":3}},"37":{"start":{"line":82,"column":2},"end":{"line":113,"column":3}},"38":{"start":{"line":83,"column":19},"end":{"line":83,"column":29}},"39":{"start":{"line":85,"column":19},"end":{"line":88,"column":5}},"40":{"start":{"line":90,"column":4},"end":{"line":92,"column":5}},"41":{"start":{"line":91,"column":6},"end":{"line":91,"column":65}},"42":{"start":{"line":94,"column":19},"end":{"line":94,"column":33}},"43":{"start":{"line":97,"column":4},"end":{"line":107,"column":5}},"44":{"start":{"line":98,"column":30},"end":{"line":101,"column":7}},"45":{"start":{"line":102,"column":6},"end":{"line":104,"column":7}},"46":{"start":{"line":103,"column":8},"end":{"line":103,"column":64}},"47":{"start":{"line":105,"column":11},"end":{"line":107,"column":5}},"48":{"start":{"line":106,"column":6},"end":{"line":106,"column":62}},"49":{"start":{"line":109,"column":4},"end":{"line":109,"column":21}},"50":{"start":{"line":111,"column":4},"end":{"line":111,"column":46}},"51":{"start":{"line":112,"column":4},"end":{"line":112,"column":62}},"52":{"start":{"line":117,"column":0},"end":{"line":165,"column":3}},"53":{"start":{"line":118,"column":2},"end":{"line":164,"column":3}},"54":{"start":{"line":119,"column":4},"end":{"line":121,"column":5}},"55":{"start":{"line":120,"column":6},"end":{"line":120,"column":65}},"56":{"start":{"line":124,"column":28},"end":{"line":127,"column":5}},"57":{"start":{"line":129,"column":4},"end":{"line":131,"column":5}},"58":{"start":{"line":130,"column":6},"end":{"line":130,"column":76}},"59":{"start":{"line":133,"column":24},"end":{"line":133,"column":50}},"60":{"start":{"line":136,"column":4},"end":{"line":141,"column":5}},"61":{"start":{"line":137,"column":6},"end":{"line":140,"column":8}},"62":{"start":{"line":143,"column":19},"end":{"line":155,"column":6}},"63":{"start":{"line":157,"column":4},"end":{"line":160,"column":7}},"64":{"start":{"line":162,"column":4},"end":{"line":162,"column":49}},"65":{"start":{"line":163,"column":4},"end":{"line":163,"column":63}},"66":{"start":{"line":168,"column":0},"end":{"line":206,"column":3}},"67":{"start":{"line":169,"column":2},"end":{"line":205,"column":3}},"68":{"start":{"line":170,"column":19},"end":{"line":170,"column":29}},"69":{"start":{"line":172,"column":19},"end":{"line":175,"column":5}},"70":{"start":{"line":177,"column":4},"end":{"line":179,"column":5}},"71":{"start":{"line":178,"column":6},"end":{"line":178,"column":65}},"72":{"start":{"line":181,"column":19},"end":{"line":181,"column":33}},"73":{"start":{"line":184,"column":4},"end":{"line":194,"column":5}},"74":{"start":{"line":185,"column":30},"end":{"line":188,"column":7}},"75":{"start":{"line":189,"column":6},"end":{"line":191,"column":7}},"76":{"start":{"line":190,"column":8},"end":{"line":190,"column":64}},"77":{"start":{"line":192,"column":11},"end":{"line":194,"column":5}},"78":{"start":{"line":193,"column":6},"end":{"line":193,"column":62}},"79":{"start":{"line":197,"column":4},"end":{"line":199,"column":5}},"80":{"start":{"line":198,"column":6},"end":{"line":198,"column":70}},"81":{"start":{"line":201,"column":4},"end":{"line":201,"column":57}},"82":{"start":{"line":203,"column":4},"end":{"line":203,"column":51}},"83":{"start":{"line":204,"column":4},"end":{"line":204,"column":65}},"84":{"start":{"line":209,"column":0},"end":{"line":255,"column":3}},"85":{"start":{"line":210,"column":2},"end":{"line":254,"column":3}},"86":{"start":{"line":211,"column":19},"end":{"line":211,"column":29}},"87":{"start":{"line":214,"column":28},"end":{"line":217,"column":5}},"88":{"start":{"line":219,"column":4},"end":{"line":221,"column":5}},"89":{"start":{"line":220,"column":6},"end":{"line":220,"column":76}},"90":{"start":{"line":223,"column":24},"end":{"line":223,"column":50}},"91":{"start":{"line":226,"column":25},"end":{"line":229,"column":5}},"92":{"start":{"line":231,"column":4},"end":{"line":233,"column":5}},"93":{"start":{"line":232,"column":6},"end":{"line":232,"column":65}},"94":{"start":{"line":236,"column":4},"end":{"line":239,"column":6}},"95":{"start":{"line":242,"column":19},"end":{"line":245,"column":5}},"96":{"start":{"line":247,"column":4},"end":{"line":250,"column":7}},"97":{"start":{"line":252,"column":4},"end":{"line":252,"column":54}},"98":{"start":{"line":253,"column":4},"end":{"line":253,"column":68}},"99":{"start":{"line":258,"column":0},"end":{"line":299,"column":3}},"100":{"start":{"line":259,"column":2},"end":{"line":298,"column":3}},"101":{"start":{"line":260,"column":19},"end":{"line":260,"column":29}},"102":{"start":{"line":263,"column":28},"end":{"line":266,"column":5}},"103":{"start":{"line":268,"column":4},"end":{"line":270,"column":5}},"104":{"start":{"line":269,"column":6},"end":{"line":269,"column":76}},"105":{"start":{"line":272,"column":24},"end":{"line":272,"column":50}},"106":{"start":{"line":275,"column":25},"end":{"line":278,"column":5}},"107":{"start":{"line":280,"column":4},"end":{"line":282,"column":5}},"108":{"start":{"line":281,"column":6},"end":{"line":281,"column":65}},"109":{"start":{"line":284,"column":19},"end":{"line":284,"column":39}},"110":{"start":{"line":287,"column":4},"end":{"line":289,"column":5}},"111":{"start":{"line":288,"column":6},"end":{"line":288,"column":38}},"112":{"start":{"line":292,"column":4},"end":{"line":292,"column":64}},"113":{"start":{"line":294,"column":4},"end":{"line":294,"column":57}},"114":{"start":{"line":296,"column":4},"end":{"line":296,"column":49}},"115":{"start":{"line":297,"column":4},"end":{"line":297,"column":63}},"116":{"start":{"line":301,"column":0},"end":{"line":301,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":15,"column":15},"end":{"line":15,"column":16}},"loc":{"start":{"line":15,"column":34},"end":{"line":21,"column":3}},"line":15},"1":{"name":"(anonymous_1)","decl":{"start":{"line":22,"column":12},"end":{"line":22,"column":13}},"loc":{"start":{"line":22,"column":31},"end":{"line":26,"column":3}},"line":22},"2":{"name":"(anonymous_2)","decl":{"start":{"line":34,"column":14},"end":{"line":34,"column":15}},"loc":{"start":{"line":34,"column":33},"end":{"line":47,"column":3}},"line":34},"3":{"name":"(anonymous_3)","decl":{"start":{"line":51,"column":57},"end":{"line":51,"column":58}},"loc":{"start":{"line":51,"column":77},"end":{"line":78,"column":1}},"line":51},"4":{"name":"(anonymous_4)","decl":{"start":{"line":81,"column":38},"end":{"line":81,"column":39}},"loc":{"start":{"line":81,"column":58},"end":{"line":114,"column":1}},"line":81},"5":{"name":"(anonymous_5)","decl":{"start":{"line":117,"column":95},"end":{"line":117,"column":96}},"loc":{"start":{"line":117,"column":115},"end":{"line":165,"column":1}},"line":117},"6":{"name":"(anonymous_6)","decl":{"start":{"line":168,"column":47},"end":{"line":168,"column":48}},"loc":{"start":{"line":168,"column":67},"end":{"line":206,"column":1}},"line":168},"7":{"name":"(anonymous_7)","decl":{"start":{"line":209,"column":74},"end":{"line":209,"column":75}},"loc":{"start":{"line":209,"column":94},"end":{"line":255,"column":1}},"line":209},"8":{"name":"(anonymous_8)","decl":{"start":{"line":258,"column":69},"end":{"line":258,"column":70}},"loc":{"start":{"line":258,"column":89},"end":{"line":299,"column":1}},"line":258}},"branchMap":{"0":{"loc":{"start":{"line":17,"column":4},"end":{"line":19,"column":5}},"type":"if","locations":[{"start":{"line":17,"column":4},"end":{"line":19,"column":5}},{"start":{},"end":{}}],"line":17},"1":{"loc":{"start":{"line":24,"column":38},"end":{"line":24,"column":62}},"type":"binary-expr","locations":[{"start":{"line":24,"column":38},"end":{"line":24,"column":50}},{"start":{"line":24,"column":54},"end":{"line":24,"column":62}}],"line":24},"2":{"loc":{"start":{"line":42,"column":4},"end":{"line":46,"column":5}},"type":"if","locations":[{"start":{"line":42,"column":4},"end":{"line":46,"column":5}},{"start":{"line":44,"column":11},"end":{"line":46,"column":5}}],"line":42},"3":{"loc":{"start":{"line":56,"column":4},"end":{"line":66,"column":5}},"type":"if","locations":[{"start":{"line":56,"column":4},"end":{"line":66,"column":5}},{"start":{"line":64,"column":11},"end":{"line":66,"column":5}}],"line":56},"4":{"loc":{"start":{"line":61,"column":6},"end":{"line":63,"column":7}},"type":"if","locations":[{"start":{"line":61,"column":6},"end":{"line":63,"column":7}},{"start":{},"end":{}}],"line":61},"5":{"loc":{"start":{"line":61,"column":10},"end":{"line":61,"column":89}},"type":"binary-expr","locations":[{"start":{"line":61,"column":10},"end":{"line":61,"column":43}},{"start":{"line":61,"column":47},"end":{"line":61,"column":89}}],"line":61},"6":{"loc":{"start":{"line":64,"column":11},"end":{"line":66,"column":5}},"type":"if","locations":[{"start":{"line":64,"column":11},"end":{"line":66,"column":5}},{"start":{},"end":{}}],"line":64},"7":{"loc":{"start":{"line":64,"column":15},"end":{"line":64,"column":105}},"type":"binary-expr","locations":[{"start":{"line":64,"column":15},"end":{"line":64,"column":40}},{"start":{"line":64,"column":44},"end":{"line":64,"column":73}},{"start":{"line":64,"column":77},"end":{"line":64,"column":105}}],"line":64},"8":{"loc":{"start":{"line":90,"column":4},"end":{"line":92,"column":5}},"type":"if","locations":[{"start":{"line":90,"column":4},"end":{"line":92,"column":5}},{"start":{},"end":{}}],"line":90},"9":{"loc":{"start":{"line":97,"column":4},"end":{"line":107,"column":5}},"type":"if","locations":[{"start":{"line":97,"column":4},"end":{"line":107,"column":5}},{"start":{"line":105,"column":11},"end":{"line":107,"column":5}}],"line":97},"10":{"loc":{"start":{"line":102,"column":6},"end":{"line":104,"column":7}},"type":"if","locations":[{"start":{"line":102,"column":6},"end":{"line":104,"column":7}},{"start":{},"end":{}}],"line":102},"11":{"loc":{"start":{"line":102,"column":10},"end":{"line":102,"column":97}},"type":"binary-expr","locations":[{"start":{"line":102,"column":10},"end":{"line":102,"column":43}},{"start":{"line":102,"column":47},"end":{"line":102,"column":97}}],"line":102},"12":{"loc":{"start":{"line":105,"column":11},"end":{"line":107,"column":5}},"type":"if","locations":[{"start":{"line":105,"column":11},"end":{"line":107,"column":5}},{"start":{},"end":{}}],"line":105},"13":{"loc":{"start":{"line":105,"column":15},"end":{"line":105,"column":105}},"type":"binary-expr","locations":[{"start":{"line":105,"column":15},"end":{"line":105,"column":40}},{"start":{"line":105,"column":44},"end":{"line":105,"column":73}},{"start":{"line":105,"column":77},"end":{"line":105,"column":105}}],"line":105},"14":{"loc":{"start":{"line":119,"column":4},"end":{"line":121,"column":5}},"type":"if","locations":[{"start":{"line":119,"column":4},"end":{"line":121,"column":5}},{"start":{},"end":{}}],"line":119},"15":{"loc":{"start":{"line":129,"column":4},"end":{"line":131,"column":5}},"type":"if","locations":[{"start":{"line":129,"column":4},"end":{"line":131,"column":5}},{"start":{},"end":{}}],"line":129},"16":{"loc":{"start":{"line":136,"column":4},"end":{"line":141,"column":5}},"type":"if","locations":[{"start":{"line":136,"column":4},"end":{"line":141,"column":5}},{"start":{},"end":{}}],"line":136},"17":{"loc":{"start":{"line":177,"column":4},"end":{"line":179,"column":5}},"type":"if","locations":[{"start":{"line":177,"column":4},"end":{"line":179,"column":5}},{"start":{},"end":{}}],"line":177},"18":{"loc":{"start":{"line":184,"column":4},"end":{"line":194,"column":5}},"type":"if","locations":[{"start":{"line":184,"column":4},"end":{"line":194,"column":5}},{"start":{"line":192,"column":11},"end":{"line":194,"column":5}}],"line":184},"19":{"loc":{"start":{"line":189,"column":6},"end":{"line":191,"column":7}},"type":"if","locations":[{"start":{"line":189,"column":6},"end":{"line":191,"column":7}},{"start":{},"end":{}}],"line":189},"20":{"loc":{"start":{"line":189,"column":10},"end":{"line":189,"column":97}},"type":"binary-expr","locations":[{"start":{"line":189,"column":10},"end":{"line":189,"column":43}},{"start":{"line":189,"column":47},"end":{"line":189,"column":97}}],"line":189},"21":{"loc":{"start":{"line":192,"column":11},"end":{"line":194,"column":5}},"type":"if","locations":[{"start":{"line":192,"column":11},"end":{"line":194,"column":5}},{"start":{},"end":{}}],"line":192},"22":{"loc":{"start":{"line":192,"column":15},"end":{"line":192,"column":105}},"type":"binary-expr","locations":[{"start":{"line":192,"column":15},"end":{"line":192,"column":40}},{"start":{"line":192,"column":44},"end":{"line":192,"column":73}},{"start":{"line":192,"column":77},"end":{"line":192,"column":105}}],"line":192},"23":{"loc":{"start":{"line":197,"column":4},"end":{"line":199,"column":5}},"type":"if","locations":[{"start":{"line":197,"column":4},"end":{"line":199,"column":5}},{"start":{},"end":{}}],"line":197},"24":{"loc":{"start":{"line":219,"column":4},"end":{"line":221,"column":5}},"type":"if","locations":[{"start":{"line":219,"column":4},"end":{"line":221,"column":5}},{"start":{},"end":{}}],"line":219},"25":{"loc":{"start":{"line":231,"column":4},"end":{"line":233,"column":5}},"type":"if","locations":[{"start":{"line":231,"column":4},"end":{"line":233,"column":5}},{"start":{},"end":{}}],"line":231},"26":{"loc":{"start":{"line":268,"column":4},"end":{"line":270,"column":5}},"type":"if","locations":[{"start":{"line":268,"column":4},"end":{"line":270,"column":5}},{"start":{},"end":{}}],"line":268},"27":{"loc":{"start":{"line":280,"column":4},"end":{"line":282,"column":5}},"type":"if","locations":[{"start":{"line":280,"column":4},"end":{"line":282,"column":5}},{"start":{},"end":{}}],"line":280},"28":{"loc":{"start":{"line":287,"column":4},"end":{"line":289,"column":5}},"type":"if","locations":[{"start":{"line":287,"column":4},"end":{"line":289,"column":5}},{"start":{},"end":{}}],"line":287}},"s":{"0":7,"1":7,"2":7,"3":7,"4":7,"5":7,"6":7,"7":7,"8":7,"9":7,"10":7,"11":1,"12":1,"13":0,"14":1,"15":1,"16":1,"17":1,"18":7,"19":1,"20":1,"21":1,"22":0,"23":7,"24":1,"25":1,"26":1,"27":1,"28":1,"29":0,"30":0,"31":0,"32":1,"33":1,"34":0,"35":0,"36":7,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":7,"53":1,"54":1,"55":0,"56":1,"57":1,"58":0,"59":1,"60":1,"61":1,"62":1,"63":1,"64":0,"65":0,"66":7,"67":1,"68":1,"69":1,"70":1,"71":0,"72":1,"73":1,"74":1,"75":1,"76":0,"77":0,"78":0,"79":1,"80":0,"81":1,"82":0,"83":0,"84":7,"85":1,"86":1,"87":1,"88":1,"89":0,"90":1,"91":1,"92":1,"93":0,"94":1,"95":1,"96":1,"97":0,"98":0,"99":7,"100":1,"101":1,"102":1,"103":1,"104":0,"105":1,"106":1,"107":1,"108":0,"109":1,"110":1,"111":1,"112":1,"113":1,"114":0,"115":0,"116":7},"f":{"0":1,"1":1,"2":1,"3":1,"4":0,"5":1,"6":1,"7":1,"8":1},"b":{"0":[0,1],"1":[1,0],"2":[1,0],"3":[1,0],"4":[0,1],"5":[1,1],"6":[0,0],"7":[0,0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0,0,0],"14":[0,1],"15":[0,1],"16":[1,0],"17":[0,1],"18":[1,0],"19":[0,1],"20":[1,1],"21":[0,0],"22":[0,0,0],"23":[0,1],"24":[0,1],"25":[0,1],"26":[0,1],"27":[0,1],"28":[1,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"85b914f5308c6ac6703ba73704fe0ce1899a244d"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/routes/users.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/routes/users.js","statementMap":{"0":{"start":{"line":1,"column":16},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":35},"end":{"line":2,"column":63}},"2":{"start":{"line":3,"column":13},"end":{"line":3,"column":46}},"3":{"start":{"line":4,"column":43},"end":{"line":4,"column":72}},"4":{"start":{"line":6,"column":15},"end":{"line":6,"column":31}},"5":{"start":{"line":9,"column":0},"end":{"line":19,"column":3}},"6":{"start":{"line":10,"column":2},"end":{"line":18,"column":3}},"7":{"start":{"line":11,"column":19},"end":{"line":13,"column":5}},"8":{"start":{"line":14,"column":4},"end":{"line":14,"column":26}},"9":{"start":{"line":16,"column":4},"end":{"line":16,"column":45}},"10":{"start":{"line":17,"column":4},"end":{"line":17,"column":61}},"11":{"start":{"line":22,"column":0},"end":{"line":45,"column":3}},"12":{"start":{"line":23,"column":2},"end":{"line":44,"column":3}},"13":{"start":{"line":24,"column":19},"end":{"line":24,"column":29}},"14":{"start":{"line":27,"column":4},"end":{"line":29,"column":5}},"15":{"start":{"line":28,"column":6},"end":{"line":28,"column":62}},"16":{"start":{"line":31,"column":19},"end":{"line":34,"column":5}},"17":{"start":{"line":36,"column":4},"end":{"line":38,"column":5}},"18":{"start":{"line":37,"column":6},"end":{"line":37,"column":63}},"19":{"start":{"line":40,"column":4},"end":{"line":40,"column":29}},"20":{"start":{"line":42,"column":4},"end":{"line":42,"column":44}},"21":{"start":{"line":43,"column":4},"end":{"line":43,"column":60}},"22":{"start":{"line":48,"column":0},"end":{"line":115,"column":3}},"23":{"start":{"line":53,"column":2},"end":{"line":114,"column":3}},"24":{"start":{"line":54,"column":19},"end":{"line":54,"column":40}},"25":{"start":{"line":55,"column":4},"end":{"line":57,"column":5}},"26":{"start":{"line":56,"column":6},"end":{"line":56,"column":62}},"27":{"start":{"line":59,"column":19},"end":{"line":59,"column":29}},"28":{"start":{"line":60,"column":43},"end":{"line":60,"column":51}},"29":{"start":{"line":63,"column":4},"end":{"line":65,"column":5}},"30":{"start":{"line":64,"column":6},"end":{"line":64,"column":62}},"31":{"start":{"line":68,"column":4},"end":{"line":76,"column":5}},"32":{"start":{"line":69,"column":27},"end":{"line":72,"column":7}},"33":{"start":{"line":73,"column":6},"end":{"line":75,"column":7}},"34":{"start":{"line":74,"column":8},"end":{"line":74,"column":71}},"35":{"start":{"line":78,"column":25},"end":{"line":78,"column":27}},"36":{"start":{"line":79,"column":25},"end":{"line":79,"column":27}},"37":{"start":{"line":80,"column":21},"end":{"line":80,"column":22}},"38":{"start":{"line":82,"column":4},"end":{"line":86,"column":5}},"39":{"start":{"line":83,"column":6},"end":{"line":83,"column":55}},"40":{"start":{"line":84,"column":6},"end":{"line":84,"column":35}},"41":{"start":{"line":85,"column":6},"end":{"line":85,"column":19}},"42":{"start":{"line":87,"column":4},"end":{"line":91,"column":5}},"43":{"start":{"line":88,"column":6},"end":{"line":88,"column":54}},"44":{"start":{"line":89,"column":6},"end":{"line":89,"column":34}},"45":{"start":{"line":90,"column":6},"end":{"line":90,"column":19}},"46":{"start":{"line":92,"column":4},"end":{"line":96,"column":5}},"47":{"start":{"line":93,"column":6},"end":{"line":93,"column":50}},"48":{"start":{"line":94,"column":6},"end":{"line":94,"column":31}},"49":{"start":{"line":95,"column":6},"end":{"line":95,"column":19}},"50":{"start":{"line":98,"column":4},"end":{"line":100,"column":5}},"51":{"start":{"line":99,"column":6},"end":{"line":99,"column":68}},"52":{"start":{"line":102,"column":4},"end":{"line":102,"column":26}},"53":{"start":{"line":103,"column":18},"end":{"line":103,"column":193}},"54":{"start":{"line":105,"column":19},"end":{"line":105,"column":56}},"55":{"start":{"line":107,"column":4},"end":{"line":110,"column":7}},"56":{"start":{"line":112,"column":4},"end":{"line":112,"column":47}},"57":{"start":{"line":113,"column":4},"end":{"line":113,"column":61}},"58":{"start":{"line":118,"column":0},"end":{"line":139,"column":3}},"59":{"start":{"line":119,"column":2},"end":{"line":138,"column":3}},"60":{"start":{"line":120,"column":19},"end":{"line":120,"column":29}},"61":{"start":{"line":122,"column":19},"end":{"line":125,"column":5}},"62":{"start":{"line":127,"column":4},"end":{"line":129,"column":5}},"63":{"start":{"line":128,"column":6},"end":{"line":128,"column":63}},"64":{"start":{"line":131,"column":4},"end":{"line":134,"column":7}},"65":{"start":{"line":136,"column":4},"end":{"line":136,"column":51}},"66":{"start":{"line":137,"column":4},"end":{"line":137,"column":65}},"67":{"start":{"line":142,"column":0},"end":{"line":163,"column":3}},"68":{"start":{"line":143,"column":2},"end":{"line":162,"column":3}},"69":{"start":{"line":144,"column":19},"end":{"line":144,"column":29}},"70":{"start":{"line":146,"column":19},"end":{"line":149,"column":5}},"71":{"start":{"line":151,"column":4},"end":{"line":153,"column":5}},"72":{"start":{"line":152,"column":6},"end":{"line":152,"column":63}},"73":{"start":{"line":155,"column":4},"end":{"line":158,"column":7}},"74":{"start":{"line":160,"column":4},"end":{"line":160,"column":49}},"75":{"start":{"line":161,"column":4},"end":{"line":161,"column":63}},"76":{"start":{"line":165,"column":0},"end":{"line":165,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":9,"column":59},"end":{"line":9,"column":60}},"loc":{"start":{"line":9,"column":79},"end":{"line":19,"column":1}},"line":9},"1":{"name":"(anonymous_1)","decl":{"start":{"line":22,"column":38},"end":{"line":22,"column":39}},"loc":{"start":{"line":22,"column":58},"end":{"line":45,"column":1}},"line":22},"2":{"name":"(anonymous_2)","decl":{"start":{"line":52,"column":3},"end":{"line":52,"column":4}},"loc":{"start":{"line":52,"column":23},"end":{"line":115,"column":1}},"line":52},"3":{"name":"(anonymous_3)","decl":{"start":{"line":118,"column":73},"end":{"line":118,"column":74}},"loc":{"start":{"line":118,"column":93},"end":{"line":139,"column":1}},"line":118},"4":{"name":"(anonymous_4)","decl":{"start":{"line":142,"column":71},"end":{"line":142,"column":72}},"loc":{"start":{"line":142,"column":91},"end":{"line":163,"column":1}},"line":142}},"branchMap":{"0":{"loc":{"start":{"line":27,"column":4},"end":{"line":29,"column":5}},"type":"if","locations":[{"start":{"line":27,"column":4},"end":{"line":29,"column":5}},{"start":{},"end":{}}],"line":27},"1":{"loc":{"start":{"line":27,"column":8},"end":{"line":27,"column":55}},"type":"binary-expr","locations":[{"start":{"line":27,"column":8},"end":{"line":27,"column":26}},{"start":{"line":27,"column":30},"end":{"line":27,"column":55}}],"line":27},"2":{"loc":{"start":{"line":36,"column":4},"end":{"line":38,"column":5}},"type":"if","locations":[{"start":{"line":36,"column":4},"end":{"line":38,"column":5}},{"start":{},"end":{}}],"line":36},"3":{"loc":{"start":{"line":55,"column":4},"end":{"line":57,"column":5}},"type":"if","locations":[{"start":{"line":55,"column":4},"end":{"line":57,"column":5}},{"start":{},"end":{}}],"line":55},"4":{"loc":{"start":{"line":63,"column":4},"end":{"line":65,"column":5}},"type":"if","locations":[{"start":{"line":63,"column":4},"end":{"line":65,"column":5}},{"start":{},"end":{}}],"line":63},"5":{"loc":{"start":{"line":63,"column":8},"end":{"line":63,"column":55}},"type":"binary-expr","locations":[{"start":{"line":63,"column":8},"end":{"line":63,"column":26}},{"start":{"line":63,"column":30},"end":{"line":63,"column":55}}],"line":63},"6":{"loc":{"start":{"line":68,"column":4},"end":{"line":76,"column":5}},"type":"if","locations":[{"start":{"line":68,"column":4},"end":{"line":76,"column":5}},{"start":{},"end":{}}],"line":68},"7":{"loc":{"start":{"line":73,"column":6},"end":{"line":75,"column":7}},"type":"if","locations":[{"start":{"line":73,"column":6},"end":{"line":75,"column":7}},{"start":{},"end":{}}],"line":73},"8":{"loc":{"start":{"line":82,"column":4},"end":{"line":86,"column":5}},"type":"if","locations":[{"start":{"line":82,"column":4},"end":{"line":86,"column":5}},{"start":{},"end":{}}],"line":82},"9":{"loc":{"start":{"line":87,"column":4},"end":{"line":91,"column":5}},"type":"if","locations":[{"start":{"line":87,"column":4},"end":{"line":91,"column":5}},{"start":{},"end":{}}],"line":87},"10":{"loc":{"start":{"line":92,"column":4},"end":{"line":96,"column":5}},"type":"if","locations":[{"start":{"line":92,"column":4},"end":{"line":96,"column":5}},{"start":{},"end":{}}],"line":92},"11":{"loc":{"start":{"line":98,"column":4},"end":{"line":100,"column":5}},"type":"if","locations":[{"start":{"line":98,"column":4},"end":{"line":100,"column":5}},{"start":{},"end":{}}],"line":98},"12":{"loc":{"start":{"line":127,"column":4},"end":{"line":129,"column":5}},"type":"if","locations":[{"start":{"line":127,"column":4},"end":{"line":129,"column":5}},{"start":{},"end":{}}],"line":127},"13":{"loc":{"start":{"line":151,"column":4},"end":{"line":153,"column":5}},"type":"if","locations":[{"start":{"line":151,"column":4},"end":{"line":153,"column":5}},{"start":{},"end":{}}],"line":151}},"s":{"0":7,"1":7,"2":7,"3":7,"4":7,"5":7,"6":1,"7":1,"8":1,"9":0,"10":0,"11":7,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":7,"23":1,"24":1,"25":1,"26":0,"27":1,"28":1,"29":1,"30":0,"31":1,"32":0,"33":0,"34":0,"35":1,"36":1,"37":1,"38":1,"39":1,"40":1,"41":1,"42":1,"43":0,"44":0,"45":0,"46":1,"47":0,"48":0,"49":0,"50":1,"51":0,"52":1,"53":1,"54":1,"55":1,"56":0,"57":0,"58":7,"59":1,"60":1,"61":1,"62":1,"63":0,"64":1,"65":0,"66":0,"67":7,"68":1,"69":1,"70":1,"71":1,"72":0,"73":1,"74":0,"75":0,"76":7},"f":{"0":1,"1":0,"2":1,"3":1,"4":1},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,1],"4":[0,1],"5":[1,0],"6":[0,1],"7":[0,0],"8":[1,0],"9":[0,1],"10":[0,1],"11":[0,1],"12":[0,1],"13":[0,1]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"f4606f96d2b5948a59a5ddc2ca6c1bc219583a3f"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/tests/utils.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/backend/src/tests/utils.js","statementMap":{"0":{"start":{"line":1,"column":23},"end":{"line":1,"column":40}},"1":{"start":{"line":2,"column":13},"end":{"line":2,"column":28}},"2":{"start":{"line":3,"column":11},"end":{"line":3,"column":24}},"3":{"start":{"line":4,"column":15},"end":{"line":4,"column":34}},"4":{"start":{"line":5,"column":16},"end":{"line":5,"column":36}},"5":{"start":{"line":6,"column":12},"end":{"line":6,"column":32}},"6":{"start":{"line":7,"column":15},"end":{"line":7,"column":35}},"7":{"start":{"line":8,"column":13},"end":{"line":8,"column":46}},"8":{"start":{"line":10,"column":20},"end":{"line":10,"column":80}},"9":{"start":{"line":10,"column":41},"end":{"line":10,"column":80}},"10":{"start":{"line":12,"column":24},"end":{"line":12,"column":38}},"11":{"start":{"line":15,"column":19},"end":{"line":22,"column":3}},"12":{"start":{"line":24,"column":19},"end":{"line":26,"column":19}},"13":{"start":{"line":28,"column":2},"end":{"line":35,"column":4}},"14":{"start":{"line":39,"column":23},"end":{"line":48,"column":3}},"15":{"start":{"line":50,"column":19},"end":{"line":53,"column":23}},"16":{"start":{"line":55,"column":2},"end":{"line":55,"column":32}},"17":{"start":{"line":59,"column":16},"end":{"line":59,"column":36}},"18":{"start":{"line":60,"column":19},"end":{"line":60,"column":34}},"19":{"start":{"line":61,"column":23},"end":{"line":61,"column":54}},"20":{"start":{"line":63,"column":17},"end":{"line":66,"column":3}},"21":{"start":{"line":68,"column":24},"end":{"line":70,"column":30}},"22":{"start":{"line":72,"column":2},"end":{"line":77,"column":4}},"23":{"start":{"line":81,"column":20},"end":{"line":81,"column":70}},"24":{"start":{"line":82,"column":2},"end":{"line":84,"column":3}},"25":{"start":{"line":83,"column":4},"end":{"line":83,"column":49}},"26":{"start":{"line":85,"column":2},"end":{"line":85,"column":19}},"27":{"start":{"line":89,"column":20},"end":{"line":89,"column":43}},"28":{"start":{"line":90,"column":2},"end":{"line":92,"column":3}},"29":{"start":{"line":91,"column":4},"end":{"line":91,"column":11}},"30":{"start":{"line":93,"column":18},"end":{"line":93,"column":43}},"31":{"start":{"line":94,"column":2},"end":{"line":101,"column":3}},"32":{"start":{"line":95,"column":21},"end":{"line":95,"column":48}},"33":{"start":{"line":96,"column":4},"end":{"line":100,"column":5}},"34":{"start":{"line":97,"column":6},"end":{"line":97,"column":30}},"35":{"start":{"line":104,"column":0},"end":{"line":112,"column":2}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":10,"column":20},"end":{"line":10,"column":21}},"loc":{"start":{"line":10,"column":41},"end":{"line":10,"column":80}},"line":10},"1":{"name":"registerUser","decl":{"start":{"line":14,"column":15},"end":{"line":14,"column":27}},"loc":{"start":{"line":14,"column":64},"end":{"line":36,"column":1}},"line":14},"2":{"name":"createEmployerProfile","decl":{"start":{"line":38,"column":15},"end":{"line":38,"column":36}},"loc":{"start":{"line":38,"column":60},"end":{"line":56,"column":1}},"line":38},"3":{"name":"createAdminUser","decl":{"start":{"line":58,"column":15},"end":{"line":58,"column":30}},"loc":{"start":{"line":58,"column":33},"end":{"line":78,"column":1}},"line":58},"4":{"name":"ensureUploadDir","decl":{"start":{"line":80,"column":15},"end":{"line":80,"column":30}},"loc":{"start":{"line":80,"column":33},"end":{"line":86,"column":1}},"line":80},"5":{"name":"cleanupUploads","decl":{"start":{"line":88,"column":15},"end":{"line":88,"column":29}},"loc":{"start":{"line":88,"column":32},"end":{"line":102,"column":1}},"line":88}},"branchMap":{"0":{"loc":{"start":{"line":10,"column":21},"end":{"line":10,"column":36}},"type":"default-arg","locations":[{"start":{"line":10,"column":30},"end":{"line":10,"column":36}}],"line":10},"1":{"loc":{"start":{"line":14,"column":28},"end":{"line":14,"column":46}},"type":"default-arg","locations":[{"start":{"line":14,"column":35},"end":{"line":14,"column":46}}],"line":14},"2":{"loc":{"start":{"line":14,"column":48},"end":{"line":14,"column":62}},"type":"default-arg","locations":[{"start":{"line":14,"column":60},"end":{"line":14,"column":62}}],"line":14},"3":{"loc":{"start":{"line":16,"column":15},"end":{"line":16,"column":44}},"type":"binary-expr","locations":[{"start":{"line":16,"column":15},"end":{"line":16,"column":34}},{"start":{"line":16,"column":38},"end":{"line":16,"column":44}}],"line":16},"4":{"loc":{"start":{"line":17,"column":14},"end":{"line":17,"column":42}},"type":"binary-expr","locations":[{"start":{"line":17,"column":14},"end":{"line":17,"column":32}},{"start":{"line":17,"column":36},"end":{"line":17,"column":42}}],"line":17},"5":{"loc":{"start":{"line":18,"column":11},"end":{"line":18,"column":47}},"type":"binary-expr","locations":[{"start":{"line":18,"column":11},"end":{"line":18,"column":26}},{"start":{"line":18,"column":30},"end":{"line":18,"column":47}}],"line":18},"6":{"loc":{"start":{"line":19,"column":14},"end":{"line":19,"column":51}},"type":"binary-expr","locations":[{"start":{"line":19,"column":14},"end":{"line":19,"column":32}},{"start":{"line":19,"column":36},"end":{"line":19,"column":51}}],"line":19},"7":{"loc":{"start":{"line":38,"column":44},"end":{"line":38,"column":58}},"type":"default-arg","locations":[{"start":{"line":38,"column":56},"end":{"line":38,"column":58}}],"line":38},"8":{"loc":{"start":{"line":40,"column":17},"end":{"line":40,"column":79}},"type":"binary-expr","locations":[{"start":{"line":40,"column":17},"end":{"line":40,"column":38}},{"start":{"line":40,"column":42},"end":{"line":40,"column":79}}],"line":40},"9":{"loc":{"start":{"line":41,"column":14},"end":{"line":41,"column":48}},"type":"binary-expr","locations":[{"start":{"line":41,"column":14},"end":{"line":41,"column":32}},{"start":{"line":41,"column":36},"end":{"line":41,"column":48}}],"line":41},"10":{"loc":{"start":{"line":42,"column":17},"end":{"line":42,"column":49}},"type":"binary-expr","locations":[{"start":{"line":42,"column":17},"end":{"line":42,"column":38}},{"start":{"line":42,"column":42},"end":{"line":42,"column":49}}],"line":42},"11":{"loc":{"start":{"line":43,"column":13},"end":{"line":43,"column":55}},"type":"binary-expr","locations":[{"start":{"line":43,"column":13},"end":{"line":43,"column":30}},{"start":{"line":43,"column":34},"end":{"line":43,"column":55}}],"line":43},"12":{"loc":{"start":{"line":44,"column":17},"end":{"line":44,"column":68}},"type":"binary-expr","locations":[{"start":{"line":44,"column":17},"end":{"line":44,"column":38}},{"start":{"line":44,"column":42},"end":{"line":44,"column":68}}],"line":44},"13":{"loc":{"start":{"line":45,"column":13},"end":{"line":45,"column":47}},"type":"binary-expr","locations":[{"start":{"line":45,"column":13},"end":{"line":45,"column":30}},{"start":{"line":45,"column":34},"end":{"line":45,"column":47}}],"line":45},"14":{"loc":{"start":{"line":46,"column":11},"end":{"line":46,"column":43}},"type":"binary-expr","locations":[{"start":{"line":46,"column":11},"end":{"line":46,"column":26}},{"start":{"line":46,"column":30},"end":{"line":46,"column":43}}],"line":46},"15":{"loc":{"start":{"line":82,"column":2},"end":{"line":84,"column":3}},"type":"if","locations":[{"start":{"line":82,"column":2},"end":{"line":84,"column":3}},{"start":{},"end":{}}],"line":82},"16":{"loc":{"start":{"line":90,"column":2},"end":{"line":92,"column":3}},"type":"if","locations":[{"start":{"line":90,"column":2},"end":{"line":92,"column":3}},{"start":{},"end":{}}],"line":90}},"s":{"0":7,"1":7,"2":7,"3":7,"4":7,"5":7,"6":7,"7":7,"8":7,"9":16,"10":7,"11":15,"12":15,"13":15,"14":11,"15":11,"16":11,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":24,"24":24,"25":0,"26":24,"27":23,"28":23,"29":0,"30":23,"31":23,"32":0,"33":0,"34":0,"35":7},"f":{"0":16,"1":15,"2":11,"3":1,"4":24,"5":23},"b":{"0":[0],"1":[0],"2":[15],"3":[15,15],"4":[15,15],"5":[15,15],"6":[15,15],"7":[10],"8":[11,10],"9":[11,10],"10":[11,11],"11":[11,11],"12":[11,11],"13":[11,11],"14":[11,11],"15":[0,24],"16":[0,23]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"181e1f97c5604cf9e79dfd7e8fd9e6a74b95c3a7"} +} diff --git a/backend/coverage/lcov-report/base.css b/backend/coverage/lcov-report/base.css new file mode 100644 index 0000000..f418035 --- /dev/null +++ b/backend/coverage/lcov-report/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/backend/coverage/lcov-report/block-navigation.js b/backend/coverage/lcov-report/block-navigation.js new file mode 100644 index 0000000..530d1ed --- /dev/null +++ b/backend/coverage/lcov-report/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selector that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/backend/coverage/lcov-report/favicon.png b/backend/coverage/lcov-report/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..c1525b811a167671e9de1fa78aab9f5c0b61cef7 GIT binary patch literal 445 zcmV;u0Yd(XP))rP{nL}Ln%S7`m{0DjX9TLF* zFCb$4Oi7vyLOydb!7n&^ItCzb-%BoB`=x@N2jll2Nj`kauio%aw_@fe&*}LqlFT43 z8doAAe))z_%=P%v^@JHp3Hjhj^6*Kr_h|g_Gr?ZAa&y>wxHE99Gk>A)2MplWz2xdG zy8VD2J|Uf#EAw*bo5O*PO_}X2Tob{%bUoO2G~T`@%S6qPyc}VkhV}UifBuRk>%5v( z)x7B{I~z*k<7dv#5tC+m{km(D087J4O%+<<;K|qwefb6@GSX45wCK}Sn*> + + + + Code coverage report for All files + + + + + + + + + +
+
+

All files

+
+ +
+ 61.6% + Statements + 661/1073 +
+ + +
+ 48.83% + Branches + 231/473 +
+ + +
+ 78.33% + Functions + 47/60 +
+ + +
+ 61.57% + Lines + 657/1067 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
src +
+
86.84%33/3837.5%3/80%0/486.84%33/38
src/config +
+
84.21%16/1964%16/25100%3/383.33%15/18
src/database +
+
72.72%8/1125%2/850%1/272.72%8/11
src/middleware +
+
85.18%23/2766.66%8/12100%3/385.18%23/27
src/routes +
+
58.38%550/94245.12%176/39080.95%34/4258.42%548/938
src/tests +
+
86.11%31/3686.66%26/30100%6/685.71%30/35
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/prettify.css b/backend/coverage/lcov-report/prettify.css new file mode 100644 index 0000000..b317a7c --- /dev/null +++ b/backend/coverage/lcov-report/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/backend/coverage/lcov-report/prettify.js b/backend/coverage/lcov-report/prettify.js new file mode 100644 index 0000000..b322523 --- /dev/null +++ b/backend/coverage/lcov-report/prettify.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/backend/coverage/lcov-report/sort-arrow-sprite.png b/backend/coverage/lcov-report/sort-arrow-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed68316eb3f65dec9063332d2f69bf3093bbfab GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^>_9Bd!3HEZxJ@+%Qh}Z>jv*C{$p!i!8j}?a+@3A= zIAGwzjijN=FBi!|L1t?LM;Q;gkwn>2cAy-KV{dn nf0J1DIvEHQu*n~6U}x}qyky7vi4|9XhBJ7&`njxgN@xNA8m%nc literal 0 HcmV?d00001 diff --git a/backend/coverage/lcov-report/sorter.js b/backend/coverage/lcov-report/sorter.js new file mode 100644 index 0000000..4ed70ae --- /dev/null +++ b/backend/coverage/lcov-report/sorter.js @@ -0,0 +1,210 @@ +/* eslint-disable */ +var addSorting = (function() { + 'use strict'; + var cols, + currentSort = { + index: 0, + desc: false + }; + + // returns the summary table element + function getTable() { + return document.querySelector('.coverage-summary'); + } + // returns the thead element of the summary table + function getTableHeader() { + return getTable().querySelector('thead tr'); + } + // returns the tbody element of the summary table + function getTableBody() { + return getTable().querySelector('tbody'); + } + // returns the th element for nth column + function getNthColumn(n) { + return getTableHeader().querySelectorAll('th')[n]; + } + + function onFilterInput() { + const searchValue = document.getElementById('fileSearch').value; + const rows = document.getElementsByTagName('tbody')[0].children; + + // Try to create a RegExp from the searchValue. If it fails (invalid regex), + // it will be treated as a plain text search + let searchRegex; + try { + searchRegex = new RegExp(searchValue, 'i'); // 'i' for case-insensitive + } catch (error) { + searchRegex = null; + } + + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + let isMatch = false; + + if (searchRegex) { + // If a valid regex was created, use it for matching + isMatch = searchRegex.test(row.textContent); + } else { + // Otherwise, fall back to the original plain text search + isMatch = row.textContent + .toLowerCase() + .includes(searchValue.toLowerCase()); + } + + row.style.display = isMatch ? '' : 'none'; + } + } + + // loads the search box + function addSearchBox() { + var template = document.getElementById('filterTemplate'); + var templateClone = template.content.cloneNode(true); + templateClone.getElementById('fileSearch').oninput = onFilterInput; + template.parentElement.appendChild(templateClone); + } + + // loads all columns + function loadColumns() { + var colNodes = getTableHeader().querySelectorAll('th'), + colNode, + cols = [], + col, + i; + + for (i = 0; i < colNodes.length; i += 1) { + colNode = colNodes[i]; + col = { + key: colNode.getAttribute('data-col'), + sortable: !colNode.getAttribute('data-nosort'), + type: colNode.getAttribute('data-type') || 'string' + }; + cols.push(col); + if (col.sortable) { + col.defaultDescSort = col.type === 'number'; + colNode.innerHTML = + colNode.innerHTML + ''; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function(a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function(a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc + ? ' sorted-desc' + : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function() { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i = 0; i < cols.length; i += 1) { + if (cols[i].sortable) { + // add the click event handler on the th so users + // dont have to click on those tiny arrows + el = getNthColumn(i).querySelector('.sorter').parentElement; + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function() { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(); + addSearchBox(); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/backend/coverage/lcov-report/src/config/index.html b/backend/coverage/lcov-report/src/config/index.html new file mode 100644 index 0000000..e62f83b --- /dev/null +++ b/backend/coverage/lcov-report/src/config/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/config + + + + + + + + + +
+
+

All files src/config

+
+ +
+ 84.21% + Statements + 16/19 +
+ + +
+ 64% + Branches + 16/25 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 83.33% + Lines + 15/18 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
index.js +
+
84.21%16/1964%16/25100%3/383.33%15/18
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/config/index.js.html b/backend/coverage/lcov-report/src/config/index.js.html new file mode 100644 index 0000000..443cfce --- /dev/null +++ b/backend/coverage/lcov-report/src/config/index.js.html @@ -0,0 +1,241 @@ + + + + + + Code coverage report for src/config/index.js + + + + + + + + + +
+
+

All files / src/config index.js

+
+ +
+ 84.21% + Statements + 16/19 +
+ + +
+ 64% + Branches + 16/25 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 83.33% + Lines + 15/18 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +537x +  +7x +  +7x +21x +21x +  +  +7x +7x +  +  +  +  +  +  +  +  +  +7x +  +7x +  +  +  +7x +  +  +7x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +7x +14x +  +7x +  +  +  +7x + 
const dotenv = require('dotenv');
+ 
+dotenv.config();
+ 
+const optionalNumber = (value, fallback) => {
+  const parsed = parseInt(value, 10);
+  return Number.isFinite(parsed) ? parsed : fallback;
+};
+ 
+const deriveDatabaseUrl = () => {
+  Iif (process.env.DATABASE_URL) {
+    return process.env.DATABASE_URL;
+  }
+ 
+  const {
+    POSTGRES_USER = 'merchantsofhope_user',
+    POSTGRES_PASSWORD,
+    POSTGRES_DB = 'merchantsofhope_supplyanddemandportal',
+    POSTGRES_HOST = 'merchantsofhope-supplyanddemandportal-database',
+    POSTGRES_PORT = '5432'
+  } = process.env;
+ 
+  Iif (!POSTGRES_PASSWORD) {
+    return undefined;
+  }
+ 
+  return `postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}`;
+};
+ 
+const config = {
+  env: process.env.NODE_ENV || 'development',
+  host: process.env.HOST || process.env.BACKEND_HOST || '0.0.0.0',
+  port: optionalNumber(process.env.PORT || process.env.BACKEND_PORT, 3001),
+  databaseUrl: deriveDatabaseUrl(),
+  jwtSecret: process.env.JWT_SECRET,
+  corsOrigin: process.env.CORS_ORIGIN || '*',
+  logLevel: process.env.LOG_LEVEL || 'info',
+  uploadDir: process.env.UPLOAD_DIR || 'uploads/resumes',
+  rateLimit: {
+    windowMs: optionalNumber(process.env.RATE_LIMIT_WINDOW_MS, 15 * 60 * 1000),
+    max: optionalNumber(process.env.RATE_LIMIT_MAX, 100)
+  }
+};
+ 
+const required = ['databaseUrl', 'jwtSecret'];
+const missingKeys = required.filter((key) => !config[key]);
+ 
+Iif (missingKeys.length > 0) {
+  throw new Error(`Missing required environment variables: ${missingKeys.join(', ')}`);
+}
+ 
+module.exports = config;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/database/connection.js.html b/backend/coverage/lcov-report/src/database/connection.js.html new file mode 100644 index 0000000..b4014f6 --- /dev/null +++ b/backend/coverage/lcov-report/src/database/connection.js.html @@ -0,0 +1,154 @@ + + + + + + Code coverage report for src/database/connection.js + + + + + + + + + +
+
+

All files / src/database connection.js

+
+ +
+ 72.72% + Statements + 8/11 +
+ + +
+ 25% + Branches + 2/8 +
+ + +
+ 50% + Functions + 1/2 +
+ + +
+ 72.72% + Lines + 8/11 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +247x +7x +  +7x +  +  +  +  +  +7x +7x +7x +  +  +  +7x +  +  +  +  +  +  +7x + 
const { Pool } = require('pg');
+const config = require('../config');
+ 
+const pool = new Pool({
+  connectionString: config.databaseUrl,
+  ssl: config.env === 'production' ? { rejectUnauthorized: false } : false
+});
+ 
+// Test database connection
+pool.on('connect', () => {
+  Eif (config.logLevel !== 'silent') {
+    console.info('Connected to MerchantsOfHope-SupplyANdDemandPortal database');
+  }
+});
+ 
+pool.on('error', (err) => {
+  if (err.code === '57P01' || err.message?.includes('terminating connection')) {
+    return;
+  }
+  console.error('Database connection error:', err);
+});
+ 
+module.exports = pool;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/database/index.html b/backend/coverage/lcov-report/src/database/index.html new file mode 100644 index 0000000..2c45a1c --- /dev/null +++ b/backend/coverage/lcov-report/src/database/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/database + + + + + + + + + +
+
+

All files src/database

+
+ +
+ 72.72% + Statements + 8/11 +
+ + +
+ 25% + Branches + 2/8 +
+ + +
+ 50% + Functions + 1/2 +
+ + +
+ 72.72% + Lines + 8/11 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
connection.js +
+
72.72%8/1125%2/850%1/272.72%8/11
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/index.html b/backend/coverage/lcov-report/src/index.html new file mode 100644 index 0000000..686a6cf --- /dev/null +++ b/backend/coverage/lcov-report/src/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src + + + + + + + + + +
+
+

All files src

+
+ +
+ 86.84% + Statements + 33/38 +
+ + +
+ 37.5% + Branches + 3/8 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 86.84% + Lines + 33/38 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
server.js +
+
86.84%33/3837.5%3/80%0/486.84%33/38
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/middleware/auth.js.html b/backend/coverage/lcov-report/src/middleware/auth.js.html new file mode 100644 index 0000000..4530c82 --- /dev/null +++ b/backend/coverage/lcov-report/src/middleware/auth.js.html @@ -0,0 +1,250 @@ + + + + + + Code coverage report for src/middleware/auth.js + + + + + + + + + +
+
+

All files / src/middleware auth.js

+
+ +
+ 85.18% + Statements + 23/27 +
+ + +
+ 66.66% + Branches + 8/12 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 85.18% + Lines + 23/27 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +567x +7x +7x +  +7x +53x +53x +  +53x +1x +  +  +52x +52x +  +  +51x +  +  +  +  +51x +  +  +  +51x +51x +  +  +  +51x +51x +  +1x +  +  +  +7x +70x +31x +  +  +  +31x +  +  +  +31x +  +  +  +7x +  +  +  + 
const jwt = require('jsonwebtoken');
+const pool = require('../database/connection');
+const config = require('../config');
+ 
+const authenticateToken = async (req, res, next) => {
+  const authHeader = req.headers['authorization'];
+  const token = authHeader && authHeader.split(' ')[1];
+ 
+  if (!token) {
+    return res.status(401).json({ error: 'Access token required' });
+  }
+ 
+  try {
+    const decoded = jwt.verify(token, config.jwtSecret);
+    
+    // Get user details from database
+    const userResult = await pool.query(
+      'SELECT id, email, first_name, last_name, role, is_active FROM users WHERE id = $1',
+      [decoded.userId]
+    );
+ 
+    Iif (userResult.rows.length === 0) {
+      return res.status(401).json({ error: 'Invalid token' });
+    }
+ 
+    const user = userResult.rows[0];
+    Iif (!user.is_active) {
+      return res.status(401).json({ error: 'Account deactivated' });
+    }
+ 
+    req.user = user;
+    next();
+  } catch (error) {
+    return res.status(403).json({ error: 'Invalid or expired token' });
+  }
+};
+ 
+const requireRole = (roles) => {
+  return (req, res, next) => {
+    Iif (!req.user) {
+      return res.status(401).json({ error: 'Authentication required' });
+    }
+ 
+    Iif (!roles.includes(req.user.role)) {
+      return res.status(403).json({ error: 'Insufficient permissions' });
+    }
+ 
+    next();
+  };
+};
+ 
+module.exports = {
+  authenticateToken,
+  requireRole
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/middleware/index.html b/backend/coverage/lcov-report/src/middleware/index.html new file mode 100644 index 0000000..c8d10db --- /dev/null +++ b/backend/coverage/lcov-report/src/middleware/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/middleware + + + + + + + + + +
+
+

All files src/middleware

+
+ +
+ 85.18% + Statements + 23/27 +
+ + +
+ 66.66% + Branches + 8/12 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 85.18% + Lines + 23/27 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
auth.js +
+
85.18%23/2766.66%8/12100%3/385.18%23/27
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/routes/applications.js.html b/backend/coverage/lcov-report/src/routes/applications.js.html new file mode 100644 index 0000000..4eec9e4 --- /dev/null +++ b/backend/coverage/lcov-report/src/routes/applications.js.html @@ -0,0 +1,1357 @@ + + + + + + Code coverage report for src/routes/applications.js + + + + + + + + + +
+
+

All files / src/routes applications.js

+
+ +
+ 59.55% + Statements + 106/178 +
+ + +
+ 38.54% + Branches + 37/96 +
+ + +
+ 100% + Functions + 5/5 +
+ + +
+ 59.55% + Lines + 106/178 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +4257x +7x +7x +7x +  +7x +  +  +7x +1x +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +  +1x +  +  +  +  +  +1x +  +  +  +  +  +1x +  +  +  +  +  +1x +  +  +  +  +  +  +1x +  +1x +  +  +  +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +  +  +1x +  +  +1x +1x +1x +1x +1x +1x +1x +  +1x +  +  +1x +  +  +  +  +  +  +1x +1x +1x +  +1x +  +  +  +  +  +1x +  +  +  +  +  +1x +  +  +  +  +  +1x +  +  +  +  +  +  +1x +1x +  +  +  +1x +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +7x +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +1x +  +  +1x +  +  +  +  +  +  +  +1x +1x +  +  +  +1x +  +  +  +  +1x +  +  +  +  +  +  +  +7x +  +  +  +  +1x +1x +1x +  +  +  +1x +  +  +1x +  +  +  +  +1x +  +  +  +1x +  +  +1x +  +  +  +  +1x +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +1x +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +7x +  +  +1x +1x +1x +  +  +  +1x +1x +  +  +1x +  +  +  +  +  +  +  +1x +  +  +  +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +1x +1x +  +  +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +7x +  +  +1x +1x +1x +  +  +  +1x +1x +  +  +1x +  +  +  +  +  +  +  +1x +  +  +  +1x +  +  +1x +  +1x +1x +  +  +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +7x + 
const express = require('express');
+const { body, validationResult } = require('express-validator');
+const pool = require('../database/connection');
+const { authenticateToken, requireRole } = require('../middleware/auth');
+ 
+const router = express.Router();
+ 
+// Get all applications (with filtering)
+router.get('/', authenticateToken, async (req, res) => {
+  try {
+    const { 
+      jobId, 
+      candidateId, 
+      status, 
+      employerId,
+      page = 1, 
+      limit = 10 
+    } = req.query;
+ 
+    let query = `
+      SELECT a.*, 
+             j.title as job_title, 
+             j.employer_id,
+             e.company_name,
+             c.user_id as candidate_user_id,
+             u.first_name, 
+             u.last_name, 
+             u.email as candidate_email
+      FROM applications a
+      JOIN jobs j ON a.job_id = j.id
+      JOIN employers e ON j.employer_id = e.id
+      JOIN candidates c ON a.candidate_id = c.id
+      JOIN users u ON c.user_id = u.id
+    `;
+    const queryParams = [];
+    let paramCount = 0;
+    const conditions = [];
+ 
+    Iif (jobId) {
+      paramCount++;
+      conditions.push(`a.job_id = $${paramCount}`);
+      queryParams.push(jobId);
+    }
+ 
+    Iif (candidateId) {
+      paramCount++;
+      conditions.push(`a.candidate_id = $${paramCount}`);
+      queryParams.push(candidateId);
+    }
+ 
+    Iif (status) {
+      paramCount++;
+      conditions.push(`a.status = $${paramCount}`);
+      queryParams.push(status);
+    }
+ 
+    Iif (employerId) {
+      paramCount++;
+      conditions.push(`j.employer_id = $${paramCount}`);
+      queryParams.push(employerId);
+    }
+ 
+    // Role-based filtering
+    if (req.user.role === 'candidate') {
+      // Candidates can only see their own applications
+      const candidateResult = await pool.query(
+        'SELECT id FROM candidates WHERE user_id = $1',
+        [req.user.id]
+      );
+      Eif (candidateResult.rows.length > 0) {
+        paramCount++;
+        conditions.push(`a.candidate_id = $${paramCount}`);
+        queryParams.push(candidateResult.rows[0].id);
+      }
+    } else Eif (req.user.role === 'employer') {
+      // Employers can only see applications for their jobs
+      const employerResult = await pool.query(
+        'SELECT id FROM employers WHERE user_id = $1',
+        [req.user.id]
+      );
+      if (employerResult.rows.length > 0) {
+        paramCount++;
+        conditions.push(`j.employer_id = $${paramCount}`);
+        queryParams.push(employerResult.rows[0].id);
+      }
+    }
+ 
+    Eif (conditions.length > 0) {
+      query += ` WHERE ${conditions.join(' AND ')}`;
+    }
+ 
+    query += ` ORDER BY a.applied_at DESC`;
+ 
+    // Add pagination
+    const offset = (page - 1) * limit;
+    paramCount++;
+    query += ` LIMIT $${paramCount}`;
+    queryParams.push(limit);
+    paramCount++;
+    query += ` OFFSET $${paramCount}`;
+    queryParams.push(offset);
+ 
+    const result = await pool.query(query, queryParams);
+ 
+    // Get total count for pagination
+    let countQuery = `
+      SELECT COUNT(*) 
+      FROM applications a
+      JOIN jobs j ON a.job_id = j.id
+      JOIN employers e ON j.employer_id = e.id
+      JOIN candidates c ON a.candidate_id = c.id
+    `;
+    const countParams = [];
+    let countParamCount = 0;
+    const countConditions = [];
+ 
+    Iif (jobId) {
+      countParamCount++;
+      countConditions.push(`a.job_id = $${countParamCount}`);
+      countParams.push(jobId);
+    }
+ 
+    Iif (candidateId) {
+      countParamCount++;
+      countConditions.push(`a.candidate_id = $${countParamCount}`);
+      countParams.push(candidateId);
+    }
+ 
+    Iif (status) {
+      countParamCount++;
+      countConditions.push(`a.status = $${countParamCount}`);
+      countParams.push(status);
+    }
+ 
+    Iif (employerId) {
+      countParamCount++;
+      countConditions.push(`j.employer_id = $${countParamCount}`);
+      countParams.push(employerId);
+    }
+ 
+    // Role-based filtering for count
+    if (req.user.role === 'candidate') {
+      const candidateResult = await pool.query(
+        'SELECT id FROM candidates WHERE user_id = $1',
+        [req.user.id]
+      );
+      Eif (candidateResult.rows.length > 0) {
+        countParamCount++;
+        countConditions.push(`a.candidate_id = $${countParamCount}`);
+        countParams.push(candidateResult.rows[0].id);
+      }
+    } else Eif (req.user.role === 'employer') {
+      const employerResult = await pool.query(
+        'SELECT id FROM employers WHERE user_id = $1',
+        [req.user.id]
+      );
+      if (employerResult.rows.length > 0) {
+        countParamCount++;
+        countConditions.push(`j.employer_id = $${countParamCount}`);
+        countParams.push(employerResult.rows[0].id);
+      }
+    }
+ 
+    Eif (countConditions.length > 0) {
+      countQuery += ` WHERE ${countConditions.join(' AND ')}`;
+    }
+ 
+    const countResult = await pool.query(countQuery, countParams);
+ 
+    res.json({
+      applications: result.rows,
+      pagination: {
+        page: parseInt(page),
+        limit: parseInt(limit),
+        total: parseInt(countResult.rows[0].count),
+        pages: Math.ceil(countResult.rows[0].count / limit)
+      }
+    });
+  } catch (error) {
+    console.error('Get applications error:', error);
+    res.status(500).json({ error: 'Failed to fetch applications' });
+  }
+});
+ 
+// Get application by ID
+router.get('/:id', authenticateToken, async (req, res) => {
+  try {
+    const { id } = req.params;
+    
+    const result = await pool.query(`
+      SELECT a.*, 
+             j.title as job_title, 
+             j.description as job_description,
+             j.employer_id,
+             e.company_name,
+             c.user_id as candidate_user_id,
+             u.first_name, 
+             u.last_name, 
+             u.email as candidate_email
+      FROM applications a
+      JOIN jobs j ON a.job_id = j.id
+      JOIN employers e ON j.employer_id = e.id
+      JOIN candidates c ON a.candidate_id = c.id
+      JOIN users u ON c.user_id = u.id
+      WHERE a.id = $1
+    `, [id]);
+ 
+    Iif (result.rows.length === 0) {
+      return res.status(404).json({ error: 'Application not found' });
+    }
+ 
+    const application = result.rows[0];
+ 
+    // Check permissions
+    Iif (req.user.role === 'candidate') {
+      const candidateResult = await pool.query(
+        'SELECT id FROM candidates WHERE user_id = $1',
+        [req.user.id]
+      );
+      if (candidateResult.rows.length === 0 || application.candidate_id !== candidateResult.rows[0].id) {
+        return res.status(403).json({ error: 'Access denied' });
+      }
+    } else Eif (req.user.role === 'employer') {
+      const employerResult = await pool.query(
+        'SELECT id FROM employers WHERE user_id = $1',
+        [req.user.id]
+      );
+      Iif (employerResult.rows.length === 0 || application.employer_id !== employerResult.rows[0].id) {
+        return res.status(403).json({ error: 'Access denied' });
+      }
+    }
+ 
+    res.json(application);
+  } catch (error) {
+    console.error('Get application error:', error);
+    res.status(500).json({ error: 'Failed to fetch application' });
+  }
+});
+ 
+// Create application
+router.post('/', authenticateToken, requireRole(['candidate']), [
+  body('jobId').isUUID(),
+  body('coverLetter').optional().trim(),
+  body('notes').optional().trim()
+], async (req, res) => {
+  try {
+    const errors = validationResult(req);
+    Iif (!errors.isEmpty()) {
+      return res.status(400).json({ errors: errors.array() });
+    }
+ 
+    const { jobId, coverLetter, notes } = req.body;
+ 
+    // Get candidate ID for the current user
+    const candidateResult = await pool.query(
+      'SELECT id FROM candidates WHERE user_id = $1',
+      [req.user.id]
+    );
+ 
+    Iif (candidateResult.rows.length === 0) {
+      return res.status(400).json({ error: 'Candidate profile not found' });
+    }
+ 
+    const candidateId = candidateResult.rows[0].id;
+ 
+    // Check if job exists and is active
+    const jobResult = await pool.query(
+      'SELECT id, status FROM jobs WHERE id = $1',
+      [jobId]
+    );
+ 
+    Iif (jobResult.rows.length === 0) {
+      return res.status(404).json({ error: 'Job not found' });
+    }
+ 
+    Iif (jobResult.rows[0].status !== 'active') {
+      return res.status(400).json({ error: 'Job is not accepting applications' });
+    }
+ 
+    // Check if application already exists
+    const existingApplication = await pool.query(
+      'SELECT id FROM applications WHERE job_id = $1 AND candidate_id = $2',
+      [jobId, candidateId]
+    );
+ 
+    Iif (existingApplication.rows.length > 0) {
+      return res.status(400).json({ error: 'Application already exists' });
+    }
+ 
+    const result = await pool.query(`
+      INSERT INTO applications (job_id, candidate_id, cover_letter, notes)
+      VALUES ($1, $2, $3, $4)
+      RETURNING *
+    `, [jobId, candidateId, coverLetter, notes]);
+ 
+    res.status(201).json({
+      message: 'Application submitted successfully',
+      application: result.rows[0]
+    });
+  } catch (error) {
+    console.error('Create application error:', error);
+    res.status(500).json({ error: 'Failed to submit application' });
+  }
+});
+ 
+// Update application status
+router.put('/:id/status', authenticateToken, [
+  body('status').isIn(['applied', 'reviewed', 'shortlisted', 'interviewed', 'offered', 'rejected', 'withdrawn'])
+], async (req, res) => {
+  try {
+    const errors = validationResult(req);
+    Iif (!errors.isEmpty()) {
+      return res.status(400).json({ errors: errors.array() });
+    }
+ 
+    const { id } = req.params;
+    const { status } = req.body;
+ 
+    // Check if application exists and user has permission
+    const applicationResult = await pool.query(`
+      SELECT a.*, j.employer_id, e.user_id as employer_user_id
+      FROM applications a
+      JOIN jobs j ON a.job_id = j.id
+      JOIN employers e ON j.employer_id = e.id
+      WHERE a.id = $1
+    `, [id]);
+ 
+    Iif (applicationResult.rows.length === 0) {
+      return res.status(404).json({ error: 'Application not found' });
+    }
+ 
+    const application = applicationResult.rows[0];
+ 
+    // Check permissions
+    Iif (req.user.role === 'candidate') {
+      const candidateResult = await pool.query(
+        'SELECT id FROM candidates WHERE user_id = $1',
+        [req.user.id]
+      );
+      if (candidateResult.rows.length === 0 || application.candidate_id !== candidateResult.rows[0].id) {
+        return res.status(403).json({ error: 'Access denied' });
+      }
+      // Candidates can only withdraw their applications
+      if (status !== 'withdrawn') {
+        return res.status(403).json({ error: 'Candidates can only withdraw applications' });
+      }
+    } else if (req.user.role === 'employer') {
+      Iif (application.employer_user_id !== req.user.id) {
+        return res.status(403).json({ error: 'Access denied' });
+      }
+    } else Eif (req.user.role !== 'admin' && req.user.role !== 'recruiter') {
+      return res.status(403).json({ error: 'Access denied' });
+    }
+ 
+    const result = await pool.query(
+      'UPDATE applications SET status = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2 RETURNING *',
+      [status, id]
+    );
+ 
+    res.json({
+      message: 'Application status updated successfully',
+      application: result.rows[0]
+    });
+  } catch (error) {
+    console.error('Update application status error:', error);
+    res.status(500).json({ error: 'Failed to update application status' });
+  }
+});
+ 
+// Update application notes
+router.put('/:id/notes', authenticateToken, [
+  body('notes').notEmpty().trim()
+], async (req, res) => {
+  try {
+    const errors = validationResult(req);
+    Iif (!errors.isEmpty()) {
+      return res.status(400).json({ errors: errors.array() });
+    }
+ 
+    const { id } = req.params;
+    const { notes } = req.body;
+ 
+    // Check if application exists and user has permission
+    const applicationResult = await pool.query(`
+      SELECT a.*, j.employer_id, e.user_id as employer_user_id
+      FROM applications a
+      JOIN jobs j ON a.job_id = j.id
+      JOIN employers e ON j.employer_id = e.id
+      WHERE a.id = $1
+    `, [id]);
+ 
+    Iif (applicationResult.rows.length === 0) {
+      return res.status(404).json({ error: 'Application not found' });
+    }
+ 
+    const application = applicationResult.rows[0];
+ 
+    // Check permissions (employers, recruiters, and admins can add notes)
+    Iif (req.user.role === 'candidate') {
+      return res.status(403).json({ error: 'Candidates cannot add notes to applications' });
+    } else if (req.user.role === 'employer') {
+      Iif (application.employer_user_id !== req.user.id) {
+        return res.status(403).json({ error: 'Access denied' });
+      }
+    } else Eif (req.user.role !== 'admin' && req.user.role !== 'recruiter') {
+      return res.status(403).json({ error: 'Access denied' });
+    }
+ 
+    const result = await pool.query(
+      'UPDATE applications SET notes = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2 RETURNING *',
+      [notes, id]
+    );
+ 
+    res.json({
+      message: 'Application notes updated successfully',
+      application: result.rows[0]
+    });
+  } catch (error) {
+    console.error('Update application notes error:', error);
+    res.status(500).json({ error: 'Failed to update application notes' });
+  }
+});
+ 
+module.exports = router;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/routes/auth.js.html b/backend/coverage/lcov-report/src/routes/auth.js.html new file mode 100644 index 0000000..3a5ab7e --- /dev/null +++ b/backend/coverage/lcov-report/src/routes/auth.js.html @@ -0,0 +1,547 @@ + + + + + + Code coverage report for src/routes/auth.js + + + + + + + + + +
+
+

All files / src/routes auth.js

+
+ +
+ 82.35% + Statements + 42/51 +
+ + +
+ 83.33% + Branches + 10/12 +
+ + +
+ 75% + Functions + 3/4 +
+ + +
+ 82.35% + Lines + 42/51 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +1557x +7x +7x +7x +7x +7x +7x +  +7x +  +  +7x +  +  +  +  +  +  +25x +25x +25x +1x +  +  +24x +  +  +24x +  +  +  +  +24x +1x +  +  +  +23x +  +  +23x +  +  +  +  +23x +  +  +23x +  +  +  +  +  +23x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +7x +  +  +  +4x +4x +4x +  +  +  +4x +  +  +4x +  +  +  +  +4x +1x +  +  +3x +  +3x +  +  +  +  +3x +3x +1x +  +  +  +2x +  +  +  +  +  +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +7x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +7x +  +  +  +7x + 
const express = require('express');
+const bcrypt = require('bcryptjs');
+const jwt = require('jsonwebtoken');
+const { body, validationResult } = require('express-validator');
+const pool = require('../database/connection');
+const { authenticateToken } = require('../middleware/auth');
+const config = require('../config');
+ 
+const router = express.Router();
+ 
+// Register
+router.post('/register', [
+  body('email').isEmail().normalizeEmail(),
+  body('password').isLength({ min: 6 }),
+  body('firstName').notEmpty().trim(),
+  body('lastName').notEmpty().trim(),
+  body('role').isIn(['recruiter', 'employer', 'candidate'])
+], async (req, res) => {
+  try {
+    const errors = validationResult(req);
+    if (!errors.isEmpty()) {
+      return res.status(400).json({ errors: errors.array() });
+    }
+ 
+    const { email, password, firstName, lastName, role } = req.body;
+ 
+    // Check if user already exists
+    const existingUser = await pool.query(
+      'SELECT id FROM users WHERE email = $1',
+      [email]
+    );
+ 
+    if (existingUser.rows.length > 0) {
+      return res.status(400).json({ error: 'User already exists' });
+    }
+ 
+    // Hash password
+    const passwordHash = await bcrypt.hash(password, 10);
+ 
+    // Create user
+    const userResult = await pool.query(
+      'INSERT INTO users (email, password_hash, first_name, last_name, role) VALUES ($1, $2, $3, $4, $5) RETURNING id, email, first_name, last_name, role',
+      [email, passwordHash, firstName, lastName, role]
+    );
+ 
+    const user = userResult.rows[0];
+ 
+    // Generate JWT token
+    const token = jwt.sign(
+      { userId: user.id, email: user.email, role: user.role },
+      config.jwtSecret,
+      { expiresIn: '24h' }
+    );
+ 
+    res.status(201).json({
+      message: 'User created successfully',
+      token,
+      user: {
+        id: user.id,
+        email: user.email,
+        firstName: user.first_name,
+        lastName: user.last_name,
+        role: user.role
+      }
+    });
+  } catch (error) {
+    console.error('Registration error:', error);
+    res.status(500).json({ error: 'Registration failed' });
+  }
+});
+ 
+// Login
+router.post('/login', [
+  body('email').isEmail().normalizeEmail(),
+  body('password').notEmpty()
+], async (req, res) => {
+  try {
+    const errors = validationResult(req);
+    Iif (!errors.isEmpty()) {
+      return res.status(400).json({ errors: errors.array() });
+    }
+ 
+    const { email, password } = req.body;
+ 
+    // Get user
+    const userResult = await pool.query(
+      'SELECT id, email, password_hash, first_name, last_name, role, is_active FROM users WHERE email = $1',
+      [email]
+    );
+ 
+    if (userResult.rows.length === 0) {
+      return res.status(401).json({ error: 'Invalid credentials' });
+    }
+ 
+    const user = userResult.rows[0];
+ 
+    Iif (!user.is_active) {
+      return res.status(401).json({ error: 'Account deactivated' });
+    }
+ 
+    // Verify password
+    const isValidPassword = await bcrypt.compare(password, user.password_hash);
+    if (!isValidPassword) {
+      return res.status(401).json({ error: 'Invalid credentials' });
+    }
+ 
+    // Generate JWT token
+    const token = jwt.sign(
+      { userId: user.id, email: user.email, role: user.role },
+      config.jwtSecret,
+      { expiresIn: '24h' }
+    );
+ 
+    res.json({
+      message: 'Login successful',
+      token,
+      user: {
+        id: user.id,
+        email: user.email,
+        firstName: user.first_name,
+        lastName: user.last_name,
+        role: user.role
+      }
+    });
+  } catch (error) {
+    console.error('Login error:', error);
+    res.status(500).json({ error: 'Login failed' });
+  }
+});
+ 
+// Get current user
+router.get('/me', authenticateToken, async (req, res) => {
+  try {
+    res.json({
+      user: {
+        id: req.user.id,
+        email: req.user.email,
+        firstName: req.user.first_name,
+        lastName: req.user.last_name,
+        role: req.user.role
+      }
+    });
+  } catch (error) {
+    console.error('Get user error:', error);
+    res.status(500).json({ error: 'Failed to get user information' });
+  }
+});
+ 
+// Logout (client-side token removal)
+router.post('/logout', authenticateToken, (req, res) => {
+  res.json({ message: 'Logout successful' });
+});
+ 
+module.exports = router;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/routes/candidates.js.html b/backend/coverage/lcov-report/src/routes/candidates.js.html new file mode 100644 index 0000000..f38ae6a --- /dev/null +++ b/backend/coverage/lcov-report/src/routes/candidates.js.html @@ -0,0 +1,1207 @@ + + + + + + Code coverage report for src/routes/candidates.js + + + + + + + + + +
+
+

All files / src/routes candidates.js

+
+ +
+ 56.81% + Statements + 100/176 +
+ + +
+ 46.66% + Branches + 28/60 +
+ + +
+ 85.71% + Functions + 6/7 +
+ + +
+ 56.32% + Lines + 98/174 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +3757x +7x +7x +7x +  +7x +  +  +7x +1x +1x +  +1x +  +  +  +  +1x +1x +1x +  +1x +1x +1x +1x +1x +  +  +1x +  +  +  +  +  +1x +1x +1x +1x +  +  +1x +1x +  +  +1x +  +  +1x +1x +1x +1x +1x +1x +1x +  +1x +  +  +1x +  +  +  +  +1x +1x +1x +  +1x +1x +1x +1x +1x +  +  +1x +  +  +  +  +  +1x +1x +1x +1x +  +  +1x +1x +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +7x +1x +1x +  +1x +  +  +  +  +  +  +1x +  +  +  +1x +  +  +  +  +  +  +  +7x +  +  +  +  +  +  +  +  +  +  +  +3x +3x +3x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +3x +  +  +3x +  +  +  +  +3x +  +  +  +3x +  +  +  +  +  +3x +  +  +  +  +  +  +  +  +  +  +7x +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +1x +1x +1x +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +1x +1x +1x +  +1x +  +  +  +  +  +1x +  +  +  +1x +1x +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +7x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +7x + 
const express = require('express');
+const { body, validationResult } = require('express-validator');
+const pool = require('../database/connection');
+const { authenticateToken, requireRole } = require('../middleware/auth');
+ 
+const router = express.Router();
+ 
+// Get all candidates
+router.get('/', authenticateToken, async (req, res) => {
+  try {
+    const { skills, experienceLevel, location, page = 1, limit = 10 } = req.query;
+ 
+    let query = `
+      SELECT c.*, u.email, u.first_name, u.last_name 
+      FROM candidates c 
+      JOIN users u ON c.user_id = u.id 
+    `;
+    const queryParams = [];
+    let paramCount = 0;
+    const conditions = [];
+ 
+    Eif (skills) {
+      const skillArray = skills.split(',').map(s => s.trim());
+      paramCount++;
+      conditions.push(`c.skills && $${paramCount}`);
+      queryParams.push(skillArray);
+    }
+ 
+    Iif (experienceLevel) {
+      paramCount++;
+      conditions.push(`c.experience_level = $${paramCount}`);
+      queryParams.push(experienceLevel);
+    }
+ 
+    Eif (location) {
+      paramCount++;
+      conditions.push(`c.location ILIKE $${paramCount}`);
+      queryParams.push(`%${location}%`);
+    }
+ 
+    Eif (conditions.length > 0) {
+      query += ` WHERE ${conditions.join(' AND ')}`;
+    }
+ 
+    query += ` ORDER BY c.created_at DESC`;
+ 
+    // Add pagination
+    const offset = (page - 1) * limit;
+    paramCount++;
+    query += ` LIMIT $${paramCount}`;
+    queryParams.push(limit);
+    paramCount++;
+    query += ` OFFSET $${paramCount}`;
+    queryParams.push(offset);
+ 
+    const result = await pool.query(query, queryParams);
+ 
+    // Get total count for pagination
+    let countQuery = `
+      SELECT COUNT(*) 
+      FROM candidates c 
+      JOIN users u ON c.user_id = u.id
+    `;
+    const countParams = [];
+    let countParamCount = 0;
+    const countConditions = [];
+ 
+    Eif (skills) {
+      const skillArray = skills.split(',').map(s => s.trim());
+      countParamCount++;
+      countConditions.push(`c.skills && $${countParamCount}`);
+      countParams.push(skillArray);
+    }
+ 
+    Iif (experienceLevel) {
+      countParamCount++;
+      countConditions.push(`c.experience_level = $${countParamCount}`);
+      countParams.push(experienceLevel);
+    }
+ 
+    Eif (location) {
+      countParamCount++;
+      countConditions.push(`c.location ILIKE $${countParamCount}`);
+      countParams.push(`%${location}%`);
+    }
+ 
+    Eif (countConditions.length > 0) {
+      countQuery += ` WHERE ${countConditions.join(' AND ')}`;
+    }
+ 
+    const countResult = await pool.query(countQuery, countParams);
+ 
+    res.json({
+      candidates: result.rows,
+      pagination: {
+        page: parseInt(page),
+        limit: parseInt(limit),
+        total: parseInt(countResult.rows[0].count),
+        pages: Math.ceil(countResult.rows[0].count / limit)
+      }
+    });
+  } catch (error) {
+    console.error('Get candidates error:', error);
+    res.status(500).json({ error: 'Failed to fetch candidates' });
+  }
+});
+ 
+// Get candidate by ID
+router.get('/:id', authenticateToken, async (req, res) => {
+  try {
+    const { id } = req.params;
+    
+    const result = await pool.query(`
+      SELECT c.*, u.email, u.first_name, u.last_name 
+      FROM candidates c 
+      JOIN users u ON c.user_id = u.id 
+      WHERE c.id = $1
+    `, [id]);
+ 
+    Iif (result.rows.length === 0) {
+      return res.status(404).json({ error: 'Candidate not found' });
+    }
+ 
+    res.json(result.rows[0]);
+  } catch (error) {
+    console.error('Get candidate error:', error);
+    res.status(500).json({ error: 'Failed to fetch candidate' });
+  }
+});
+ 
+// Create candidate profile
+router.post('/', authenticateToken, requireRole(['candidate']), [
+  body('phone').optional().trim(),
+  body('location').optional().trim(),
+  body('linkedinUrl').optional().isURL(),
+  body('githubUrl').optional().isURL(),
+  body('portfolioUrl').optional().isURL(),
+  body('bio').optional().trim(),
+  body('skills').optional().isArray(),
+  body('experienceLevel').optional().isIn(['entry', 'mid', 'senior', 'lead', 'executive']),
+  body('availability').optional().trim(),
+  body('salaryExpectation').optional().isInt({ min: 0 })
+], async (req, res) => {
+  try {
+    const errors = validationResult(req);
+    Iif (!errors.isEmpty()) {
+      return res.status(400).json({ errors: errors.array() });
+    }
+ 
+    const {
+      phone,
+      location,
+      linkedinUrl,
+      githubUrl,
+      portfolioUrl,
+      bio,
+      skills,
+      experienceLevel,
+      availability,
+      salaryExpectation
+    } = req.body;
+ 
+    // Check if candidate profile already exists for this user
+    const existingCandidate = await pool.query(
+      'SELECT id FROM candidates WHERE user_id = $1',
+      [req.user.id]
+    );
+ 
+    Iif (existingCandidate.rows.length > 0) {
+      return res.status(400).json({ error: 'Candidate profile already exists' });
+    }
+ 
+    const result = await pool.query(`
+      INSERT INTO candidates (user_id, phone, location, linkedin_url, github_url, portfolio_url, bio, skills, experience_level, availability, salary_expectation)
+      VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
+      RETURNING *
+    `, [req.user.id, phone, location, linkedinUrl, githubUrl, portfolioUrl, bio, skills, experienceLevel, availability, salaryExpectation]);
+ 
+    res.status(201).json({
+      message: 'Candidate profile created successfully',
+      candidate: result.rows[0]
+    });
+  } catch (error) {
+    console.error('Create candidate error:', error);
+    res.status(500).json({ error: 'Failed to create candidate profile' });
+  }
+});
+ 
+// Update candidate profile
+router.put('/:id', authenticateToken, [
+  body('phone').optional().trim(),
+  body('location').optional().trim(),
+  body('linkedinUrl').optional().isURL(),
+  body('githubUrl').optional().isURL(),
+  body('portfolioUrl').optional().isURL(),
+  body('bio').optional().trim(),
+  body('skills').optional().isArray(),
+  body('experienceLevel').optional().isIn(['entry', 'mid', 'senior', 'lead', 'executive']),
+  body('availability').optional().trim(),
+  body('salaryExpectation').optional().isInt({ min: 0 })
+], async (req, res) => {
+  try {
+    const errors = validationResult(req);
+    Iif (!errors.isEmpty()) {
+      return res.status(400).json({ errors: errors.array() });
+    }
+ 
+    const { id } = req.params;
+    const {
+      phone,
+      location,
+      linkedinUrl,
+      githubUrl,
+      portfolioUrl,
+      bio,
+      skills,
+      experienceLevel,
+      availability,
+      salaryExpectation
+    } = req.body;
+ 
+    // Check if candidate exists and user has permission
+    const candidateResult = await pool.query(
+      'SELECT user_id FROM candidates WHERE id = $1',
+      [id]
+    );
+ 
+    Iif (candidateResult.rows.length === 0) {
+      return res.status(404).json({ error: 'Candidate not found' });
+    }
+ 
+    // Users can only update their own candidate profile unless they're admin
+    Iif (candidateResult.rows[0].user_id !== req.user.id && req.user.role !== 'admin') {
+      return res.status(403).json({ error: 'Access denied' });
+    }
+ 
+    const updateFields = [];
+    const updateValues = [];
+    let paramCount = 1;
+ 
+    Iif (phone !== undefined) {
+      updateFields.push(`phone = $${paramCount}`);
+      updateValues.push(phone);
+      paramCount++;
+    }
+    Iif (location !== undefined) {
+      updateFields.push(`location = $${paramCount}`);
+      updateValues.push(location);
+      paramCount++;
+    }
+    Iif (linkedinUrl !== undefined) {
+      updateFields.push(`linkedin_url = $${paramCount}`);
+      updateValues.push(linkedinUrl);
+      paramCount++;
+    }
+    Iif (githubUrl !== undefined) {
+      updateFields.push(`github_url = $${paramCount}`);
+      updateValues.push(githubUrl);
+      paramCount++;
+    }
+    Iif (portfolioUrl !== undefined) {
+      updateFields.push(`portfolio_url = $${paramCount}`);
+      updateValues.push(portfolioUrl);
+      paramCount++;
+    }
+    Iif (bio !== undefined) {
+      updateFields.push(`bio = $${paramCount}`);
+      updateValues.push(bio);
+      paramCount++;
+    }
+    Iif (skills !== undefined) {
+      updateFields.push(`skills = $${paramCount}`);
+      updateValues.push(skills);
+      paramCount++;
+    }
+    Iif (experienceLevel !== undefined) {
+      updateFields.push(`experience_level = $${paramCount}`);
+      updateValues.push(experienceLevel);
+      paramCount++;
+    }
+    Eif (availability !== undefined) {
+      updateFields.push(`availability = $${paramCount}`);
+      updateValues.push(availability);
+      paramCount++;
+    }
+    Iif (salaryExpectation !== undefined) {
+      updateFields.push(`salary_expectation = $${paramCount}`);
+      updateValues.push(salaryExpectation);
+      paramCount++;
+    }
+ 
+    Iif (updateFields.length === 0) {
+      return res.status(400).json({ error: 'No fields to update' });
+    }
+ 
+    updateValues.push(id);
+    const query = `UPDATE candidates SET ${updateFields.join(', ')}, updated_at = CURRENT_TIMESTAMP WHERE id = $${paramCount} RETURNING *`;
+ 
+    const result = await pool.query(query, updateValues);
+ 
+    res.json({
+      message: 'Candidate profile updated successfully',
+      candidate: result.rows[0]
+    });
+  } catch (error) {
+    console.error('Update candidate error:', error);
+    res.status(500).json({ error: 'Failed to update candidate profile' });
+  }
+});
+ 
+// Get candidate's applications
+router.get('/:id/applications', authenticateToken, async (req, res) => {
+  try {
+    const { id } = req.params;
+    const { status, page = 1, limit = 10 } = req.query;
+ 
+    let query = `
+      SELECT a.*, j.title as job_title, j.employer_id, e.company_name
+      FROM applications a
+      JOIN jobs j ON a.job_id = j.id
+      JOIN employers e ON j.employer_id = e.id
+      WHERE a.candidate_id = $1
+    `;
+    const queryParams = [id];
+    let paramCount = 1;
+ 
+    if (status) {
+      paramCount++;
+      query += ` AND a.status = $${paramCount}`;
+      queryParams.push(status);
+    }
+ 
+    query += ` ORDER BY a.applied_at DESC`;
+ 
+    // Add pagination
+    const offset = (page - 1) * limit;
+    paramCount++;
+    query += ` LIMIT $${paramCount}`;
+    queryParams.push(limit);
+    paramCount++;
+    query += ` OFFSET $${paramCount}`;
+    queryParams.push(offset);
+ 
+    const result = await pool.query(query, queryParams);
+ 
+    // Get total count for pagination
+    let countQuery = `
+      SELECT COUNT(*) 
+      FROM applications a
+      WHERE a.candidate_id = $1
+    `;
+    const countParams = [id];
+    if (status) {
+      countQuery += ' AND a.status = $2';
+      countParams.push(status);
+    }
+    const countResult = await pool.query(countQuery, countParams);
+ 
+    res.json({
+      applications: result.rows,
+      pagination: {
+        page: parseInt(page),
+        limit: parseInt(limit),
+        total: parseInt(countResult.rows[0].count),
+        pages: Math.ceil(countResult.rows[0].count / limit)
+      }
+    });
+  } catch (error) {
+    console.error('Get candidate applications error:', error);
+    res.status(500).json({ error: 'Failed to fetch candidate applications' });
+  }
+});
+ 
+module.exports = router;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/routes/employers.js.html b/backend/coverage/lcov-report/src/routes/employers.js.html new file mode 100644 index 0000000..3f609aa --- /dev/null +++ b/backend/coverage/lcov-report/src/routes/employers.js.html @@ -0,0 +1,853 @@ + + + + + + Code coverage report for src/routes/employers.js + + + + + + + + + +
+
+

All files / src/routes employers.js

+
+ +
+ 42.1% + Statements + 48/114 +
+ + +
+ 38.88% + Branches + 14/36 +
+ + +
+ 60% + Functions + 3/5 +
+ + +
+ 42.1% + Lines + 48/114 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +2577x +7x +7x +7x +  +7x +  +  +7x +1x +1x +  +  +  +  +  +1x +  +  +  +  +  +  +  +7x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +7x +  +  +  +  +  +  +  +  +11x +11x +11x +  +  +  +  +  +  +  +  +  +  +  +11x +  +  +11x +  +  +  +  +11x +  +  +  +11x +  +  +  +  +  +11x +  +  +  +  +  +  +  +  +  +  +7x +  +  +  +  +  +  +  +  +1x +1x +1x +  +  +  +1x +  +  +  +  +  +  +  +  +1x +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +1x +1x +1x +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +1x +1x +1x +  +1x +  +  +  +  +1x +  +  +  +  +  +1x +  +  +  +1x +1x +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +7x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +7x + 
const express = require('express');
+const { body, validationResult } = require('express-validator');
+const pool = require('../database/connection');
+const { authenticateToken, requireRole } = require('../middleware/auth');
+ 
+const router = express.Router();
+ 
+// Get all employers
+router.get('/', authenticateToken, async (req, res) => {
+  try {
+    const result = await pool.query(`
+      SELECT e.*, u.email, u.first_name, u.last_name 
+      FROM employers e 
+      JOIN users u ON e.user_id = u.id 
+      ORDER BY e.created_at DESC
+    `);
+    res.json(result.rows);
+  } catch (error) {
+    console.error('Get employers error:', error);
+    res.status(500).json({ error: 'Failed to fetch employers' });
+  }
+});
+ 
+// Get employer by ID
+router.get('/:id', authenticateToken, async (req, res) => {
+  try {
+    const { id } = req.params;
+    
+    const result = await pool.query(`
+      SELECT e.*, u.email, u.first_name, u.last_name 
+      FROM employers e 
+      JOIN users u ON e.user_id = u.id 
+      WHERE e.id = $1
+    `, [id]);
+ 
+    if (result.rows.length === 0) {
+      return res.status(404).json({ error: 'Employer not found' });
+    }
+ 
+    res.json(result.rows[0]);
+  } catch (error) {
+    console.error('Get employer error:', error);
+    res.status(500).json({ error: 'Failed to fetch employer' });
+  }
+});
+ 
+// Create employer profile
+router.post('/', authenticateToken, requireRole(['employer']), [
+  body('companyName').notEmpty().trim(),
+  body('industry').optional().trim(),
+  body('companySize').optional().trim(),
+  body('website').optional().isURL(),
+  body('description').optional().trim(),
+  body('address').optional().trim(),
+  body('phone').optional().trim()
+], async (req, res) => {
+  try {
+    const errors = validationResult(req);
+    Iif (!errors.isEmpty()) {
+      return res.status(400).json({ errors: errors.array() });
+    }
+ 
+    const {
+      companyName,
+      industry,
+      companySize,
+      website,
+      description,
+      address,
+      phone
+    } = req.body;
+ 
+    // Check if employer profile already exists for this user
+    const existingEmployer = await pool.query(
+      'SELECT id FROM employers WHERE user_id = $1',
+      [req.user.id]
+    );
+ 
+    Iif (existingEmployer.rows.length > 0) {
+      return res.status(400).json({ error: 'Employer profile already exists' });
+    }
+ 
+    const result = await pool.query(`
+      INSERT INTO employers (user_id, company_name, industry, company_size, website, description, address, phone)
+      VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
+      RETURNING *
+    `, [req.user.id, companyName, industry, companySize, website, description, address, phone]);
+ 
+    res.status(201).json({
+      message: 'Employer profile created successfully',
+      employer: result.rows[0]
+    });
+  } catch (error) {
+    console.error('Create employer error:', error);
+    res.status(500).json({ error: 'Failed to create employer profile' });
+  }
+});
+ 
+// Update employer profile
+router.put('/:id', authenticateToken, [
+  body('companyName').optional().notEmpty().trim(),
+  body('industry').optional().trim(),
+  body('companySize').optional().trim(),
+  body('website').optional().isURL(),
+  body('description').optional().trim(),
+  body('address').optional().trim(),
+  body('phone').optional().trim()
+], async (req, res) => {
+  try {
+    const errors = validationResult(req);
+    Iif (!errors.isEmpty()) {
+      return res.status(400).json({ errors: errors.array() });
+    }
+ 
+    const { id } = req.params;
+    const {
+      companyName,
+      industry,
+      companySize,
+      website,
+      description,
+      address,
+      phone
+    } = req.body;
+ 
+    // Check if employer exists and user has permission
+    const employerResult = await pool.query(
+      'SELECT user_id FROM employers WHERE id = $1',
+      [id]
+    );
+ 
+    Iif (employerResult.rows.length === 0) {
+      return res.status(404).json({ error: 'Employer not found' });
+    }
+ 
+    // Users can only update their own employer profile unless they're admin
+    Iif (employerResult.rows[0].user_id !== req.user.id && req.user.role !== 'admin') {
+      return res.status(403).json({ error: 'Access denied' });
+    }
+ 
+    const updateFields = [];
+    const updateValues = [];
+    let paramCount = 1;
+ 
+    Iif (companyName) {
+      updateFields.push(`company_name = $${paramCount}`);
+      updateValues.push(companyName);
+      paramCount++;
+    }
+    Iif (industry !== undefined) {
+      updateFields.push(`industry = $${paramCount}`);
+      updateValues.push(industry);
+      paramCount++;
+    }
+    Iif (companySize !== undefined) {
+      updateFields.push(`company_size = $${paramCount}`);
+      updateValues.push(companySize);
+      paramCount++;
+    }
+    Iif (website !== undefined) {
+      updateFields.push(`website = $${paramCount}`);
+      updateValues.push(website);
+      paramCount++;
+    }
+    Eif (description !== undefined) {
+      updateFields.push(`description = $${paramCount}`);
+      updateValues.push(description);
+      paramCount++;
+    }
+    Iif (address !== undefined) {
+      updateFields.push(`address = $${paramCount}`);
+      updateValues.push(address);
+      paramCount++;
+    }
+    Iif (phone !== undefined) {
+      updateFields.push(`phone = $${paramCount}`);
+      updateValues.push(phone);
+      paramCount++;
+    }
+ 
+    Iif (updateFields.length === 0) {
+      return res.status(400).json({ error: 'No fields to update' });
+    }
+ 
+    updateValues.push(id);
+    const query = `UPDATE employers SET ${updateFields.join(', ')}, updated_at = CURRENT_TIMESTAMP WHERE id = $${paramCount} RETURNING *`;
+ 
+    const result = await pool.query(query, updateValues);
+ 
+    res.json({
+      message: 'Employer profile updated successfully',
+      employer: result.rows[0]
+    });
+  } catch (error) {
+    console.error('Update employer error:', error);
+    res.status(500).json({ error: 'Failed to update employer profile' });
+  }
+});
+ 
+// Get employer's jobs
+router.get('/:id/jobs', authenticateToken, async (req, res) => {
+  try {
+    const { id } = req.params;
+    const { status, page = 1, limit = 10 } = req.query;
+ 
+    let query = `
+      SELECT * FROM jobs 
+      WHERE employer_id = $1
+    `;
+    const queryParams = [id];
+    let paramCount = 1;
+ 
+    if (status) {
+      paramCount++;
+      query += ` AND status = $${paramCount}`;
+      queryParams.push(status);
+    }
+ 
+    query += ` ORDER BY created_at DESC`;
+ 
+    // Add pagination
+    const offset = (page - 1) * limit;
+    paramCount++;
+    query += ` LIMIT $${paramCount}`;
+    queryParams.push(limit);
+    paramCount++;
+    query += ` OFFSET $${paramCount}`;
+    queryParams.push(offset);
+ 
+    const result = await pool.query(query, queryParams);
+ 
+    // Get total count for pagination
+    let countQuery = 'SELECT COUNT(*) FROM jobs WHERE employer_id = $1';
+    const countParams = [id];
+    if (status) {
+      countQuery += ' AND status = $2';
+      countParams.push(status);
+    }
+    const countResult = await pool.query(countQuery, countParams);
+ 
+    res.json({
+      jobs: result.rows,
+      pagination: {
+        page: parseInt(page),
+        limit: parseInt(limit),
+        total: parseInt(countResult.rows[0].count),
+        pages: Math.ceil(countResult.rows[0].count / limit)
+      }
+    });
+  } catch (error) {
+    console.error('Get employer jobs error:', error);
+    res.status(500).json({ error: 'Failed to fetch employer jobs' });
+  }
+});
+ 
+module.exports = router;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/routes/index.html b/backend/coverage/lcov-report/src/routes/index.html new file mode 100644 index 0000000..1beb8de --- /dev/null +++ b/backend/coverage/lcov-report/src/routes/index.html @@ -0,0 +1,206 @@ + + + + + + Code coverage report for src/routes + + + + + + + + + +
+
+

All files src/routes

+
+ +
+ 58.38% + Statements + 550/942 +
+ + +
+ 45.12% + Branches + 176/390 +
+ + +
+ 80.95% + Functions + 34/42 +
+ + +
+ 58.42% + Lines + 548/938 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
applications.js +
+
59.55%106/17838.54%37/96100%5/559.55%106/178
auth.js +
+
82.35%42/5183.33%10/1275%3/482.35%42/51
candidates.js +
+
56.81%100/17646.66%28/6085.71%6/756.32%98/174
employers.js +
+
42.1%48/11438.88%14/3660%3/542.1%48/114
jobs.js +
+
58.07%133/22957.73%56/9771.42%5/758.59%133/227
resumes.js +
+
64.95%76/11734.42%21/6188.88%8/964.95%76/117
users.js +
+
58.44%45/7735.71%10/2880%4/558.44%45/77
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/routes/jobs.js.html b/backend/coverage/lcov-report/src/routes/jobs.js.html new file mode 100644 index 0000000..0b55fdb --- /dev/null +++ b/backend/coverage/lcov-report/src/routes/jobs.js.html @@ -0,0 +1,1519 @@ + + + + + + Code coverage report for src/routes/jobs.js + + + + + + + + + +
+
+

All files / src/routes jobs.js

+
+ +
+ 58.07% + Statements + 133/229 +
+ + +
+ 57.73% + Branches + 56/97 +
+ + +
+ 71.42% + Functions + 5/7 +
+ + +
+ 58.59% + Lines + 133/227 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +4797x +7x +7x +7x +  +7x +  +  +7x +3x +  +  +  +  +  +  +  +  +  +  +  +  +3x +  +3x +  +  +  +  +3x +3x +3x +  +  +3x +3x +3x +  +3x +1x +1x +1x +  +  +3x +1x +1x +1x +  +  +3x +  +  +  +  +  +3x +  +  +  +  +  +3x +  +  +  +  +  +  +3x +  +  +  +  +  +3x +  +  +  +  +  +3x +  +  +  +3x +3x +  +  +3x +  +  +3x +3x +3x +3x +3x +3x +3x +  +3x +  +  +3x +  +  +  +  +3x +3x +3x +  +3x +3x +3x +  +3x +1x +1x +1x +  +  +3x +1x +1x +1x +  +  +3x +  +  +  +  +  +3x +  +  +  +  +  +3x +  +  +  +  +  +  +3x +  +  +  +  +  +3x +  +  +  +  +  +3x +  +  +  +3x +3x +  +  +3x +  +3x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +7x +2x +2x +  +2x +  +  +  +  +  +  +2x +1x +  +  +1x +  +  +  +  +  +  +  +7x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +10x +10x +10x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +9x +  +  +  +9x +9x +  +  +  +9x +  +  +9x +  +  +  +  +  +  +  +  +  +9x +  +  +  +  +  +9x +  +  +  +  +  +  +  +  +  +  +7x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +1x +  +  +  +  +  +  +1x +  +  +  +1x +  +  +1x +  +  +  +1x +1x +1x +  +1x +1x +1x +1x +  +1x +1x +1x +1x +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +  +1x +  +  +  +1x +1x +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +7x +1x +1x +  +  +1x +  +  +  +  +  +  +1x +  +  +  +1x +  +  +1x +  +  +  +1x +  +1x +  +  +  +  +  +  +7x + 
const express = require('express');
+const { body, validationResult } = require('express-validator');
+const pool = require('../database/connection');
+const { authenticateToken, requireRole } = require('../middleware/auth');
+ 
+const router = express.Router();
+ 
+// Get all jobs with filtering and search
+router.get('/', authenticateToken, async (req, res) => {
+  try {
+    const { 
+      search, 
+      location, 
+      employmentType, 
+      experienceLevel, 
+      skills, 
+      salaryMin, 
+      salaryMax, 
+      remoteAllowed,
+      status = 'active',
+      page = 1, 
+      limit = 10 
+    } = req.query;
+ 
+    let query = `
+      SELECT j.*, e.company_name, e.industry, e.company_size
+      FROM jobs j
+      JOIN employers e ON j.employer_id = e.id
+    `;
+    const queryParams = [];
+    let paramCount = 0;
+    const conditions = [];
+ 
+    // Always filter by status
+    paramCount++;
+    conditions.push(`j.status = $${paramCount}`);
+    queryParams.push(status);
+ 
+    if (search) {
+      paramCount++;
+      conditions.push(`(j.title ILIKE $${paramCount} OR j.description ILIKE $${paramCount})`);
+      queryParams.push(`%${search}%`);
+    }
+ 
+    if (location) {
+      paramCount++;
+      conditions.push(`j.location ILIKE $${paramCount}`);
+      queryParams.push(`%${location}%`);
+    }
+ 
+    Iif (employmentType) {
+      paramCount++;
+      conditions.push(`j.employment_type = $${paramCount}`);
+      queryParams.push(employmentType);
+    }
+ 
+    Iif (experienceLevel) {
+      paramCount++;
+      conditions.push(`j.experience_level = $${paramCount}`);
+      queryParams.push(experienceLevel);
+    }
+ 
+    Iif (skills) {
+      const skillArray = skills.split(',').map(s => s.trim());
+      paramCount++;
+      conditions.push(`j.skills_required && $${paramCount}`);
+      queryParams.push(skillArray);
+    }
+ 
+    Iif (salaryMin) {
+      paramCount++;
+      conditions.push(`j.salary_max >= $${paramCount}`);
+      queryParams.push(parseInt(salaryMin));
+    }
+ 
+    Iif (salaryMax) {
+      paramCount++;
+      conditions.push(`j.salary_min <= $${paramCount}`);
+      queryParams.push(parseInt(salaryMax));
+    }
+ 
+    Iif (remoteAllowed === 'true') {
+      conditions.push(`j.remote_allowed = true`);
+    }
+ 
+    Eif (conditions.length > 0) {
+      query += ` WHERE ${conditions.join(' AND ')}`;
+    }
+ 
+    query += ` ORDER BY j.created_at DESC`;
+ 
+    // Add pagination
+    const offset = (page - 1) * limit;
+    paramCount++;
+    query += ` LIMIT $${paramCount}`;
+    queryParams.push(limit);
+    paramCount++;
+    query += ` OFFSET $${paramCount}`;
+    queryParams.push(offset);
+ 
+    const result = await pool.query(query, queryParams);
+ 
+    // Get total count for pagination
+    let countQuery = `
+      SELECT COUNT(*) 
+      FROM jobs j
+      JOIN employers e ON j.employer_id = e.id
+    `;
+    const countParams = [];
+    let countParamCount = 0;
+    const countConditions = [];
+ 
+    countParamCount++;
+    countConditions.push(`j.status = $${countParamCount}`);
+    countParams.push(status);
+ 
+    if (search) {
+      countParamCount++;
+      countConditions.push(`(j.title ILIKE $${countParamCount} OR j.description ILIKE $${countParamCount})`);
+      countParams.push(`%${search}%`);
+    }
+ 
+    if (location) {
+      countParamCount++;
+      countConditions.push(`j.location ILIKE $${countParamCount}`);
+      countParams.push(`%${location}%`);
+    }
+ 
+    Iif (employmentType) {
+      countParamCount++;
+      countConditions.push(`j.employment_type = $${countParamCount}`);
+      countParams.push(employmentType);
+    }
+ 
+    Iif (experienceLevel) {
+      countParamCount++;
+      countConditions.push(`j.experience_level = $${countParamCount}`);
+      countParams.push(experienceLevel);
+    }
+ 
+    Iif (skills) {
+      const skillArray = skills.split(',').map(s => s.trim());
+      countParamCount++;
+      countConditions.push(`j.skills_required && $${countParamCount}`);
+      countParams.push(skillArray);
+    }
+ 
+    Iif (salaryMin) {
+      countParamCount++;
+      countConditions.push(`j.salary_max >= $${countParamCount}`);
+      countParams.push(parseInt(salaryMin));
+    }
+ 
+    Iif (salaryMax) {
+      countParamCount++;
+      countConditions.push(`j.salary_min <= $${countParamCount}`);
+      countParams.push(parseInt(salaryMax));
+    }
+ 
+    Iif (remoteAllowed === 'true') {
+      countConditions.push(`j.remote_allowed = true`);
+    }
+ 
+    Eif (countConditions.length > 0) {
+      countQuery += ` WHERE ${countConditions.join(' AND ')}`;
+    }
+ 
+    const countResult = await pool.query(countQuery, countParams);
+ 
+    res.json({
+      jobs: result.rows,
+      pagination: {
+        page: parseInt(page),
+        limit: parseInt(limit),
+        total: parseInt(countResult.rows[0].count),
+        pages: Math.ceil(countResult.rows[0].count / limit)
+      }
+    });
+  } catch (error) {
+    console.error('Get jobs error:', error);
+    res.status(500).json({ error: 'Failed to fetch jobs' });
+  }
+});
+ 
+// Get job by ID
+router.get('/:id', authenticateToken, async (req, res) => {
+  try {
+    const { id } = req.params;
+    
+    const result = await pool.query(`
+      SELECT j.*, e.company_name, e.industry, e.company_size, e.website, e.description as company_description
+      FROM jobs j
+      JOIN employers e ON j.employer_id = e.id
+      WHERE j.id = $1
+    `, [id]);
+ 
+    if (result.rows.length === 0) {
+      return res.status(404).json({ error: 'Job not found' });
+    }
+ 
+    res.json(result.rows[0]);
+  } catch (error) {
+    console.error('Get job error:', error);
+    res.status(500).json({ error: 'Failed to fetch job' });
+  }
+});
+ 
+// Create job posting
+router.post('/', authenticateToken, requireRole(['employer', 'recruiter']), [
+  body('title').notEmpty().trim(),
+  body('description').notEmpty().trim(),
+  body('requirements').isArray(),
+  body('responsibilities').isArray(),
+  body('location').notEmpty().trim(),
+  body('employmentType').isIn(['full-time', 'part-time', 'contract', 'internship']),
+  body('salaryMin').optional().isInt({ min: 0 }),
+  body('salaryMax').optional().isInt({ min: 0 }),
+  body('currency').optional().isLength({ min: 3, max: 3 }),
+  body('remoteAllowed').optional().isBoolean(),
+  body('experienceLevel').optional().isIn(['entry', 'mid', 'senior', 'lead', 'executive']),
+  body('skillsRequired').optional().isArray(),
+  body('benefits').optional().isArray(),
+  body('applicationDeadline').optional().isISO8601()
+], async (req, res) => {
+  try {
+    const errors = validationResult(req);
+    if (!errors.isEmpty()) {
+      return res.status(400).json({ errors: errors.array() });
+    }
+ 
+    const {
+      title,
+      description,
+      requirements,
+      responsibilities,
+      location,
+      employmentType,
+      salaryMin,
+      salaryMax,
+      currency = 'USD',
+      remoteAllowed = false,
+      experienceLevel,
+      skillsRequired,
+      benefits,
+      applicationDeadline
+    } = req.body;
+ 
+    // Get employer_id for the current user
+    let employerId;
+    if (req.user.role === 'employer') {
+      const employerResult = await pool.query(
+        'SELECT id FROM employers WHERE user_id = $1',
+        [req.user.id]
+      );
+      Iif (employerResult.rows.length === 0) {
+        return res.status(400).json({ error: 'Employer profile not found' });
+      }
+      employerId = employerResult.rows[0].id;
+    } else E{
+      // For recruiters, they need to specify which employer
+      const { employerId: providedEmployerId } = req.body;
+      if (!providedEmployerId) {
+        return res.status(400).json({ error: 'Employer ID required for recruiters' });
+      }
+      employerId = providedEmployerId;
+    }
+ 
+    const result = await pool.query(`
+      INSERT INTO jobs (employer_id, title, description, requirements, responsibilities, location, employment_type, salary_min, salary_max, currency, remote_allowed, experience_level, skills_required, benefits, application_deadline)
+      VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
+      RETURNING *
+    `, [employerId, title, description, requirements, responsibilities, location, employmentType, salaryMin, salaryMax, currency, remoteAllowed, experienceLevel, skillsRequired, benefits, applicationDeadline]);
+ 
+    res.status(201).json({
+      message: 'Job posting created successfully',
+      job: result.rows[0]
+    });
+  } catch (error) {
+    console.error('Create job error:', error);
+    res.status(500).json({ error: 'Failed to create job posting' });
+  }
+});
+ 
+// Update job posting
+router.put('/:id', authenticateToken, [
+  body('title').optional().notEmpty().trim(),
+  body('description').optional().notEmpty().trim(),
+  body('requirements').optional().isArray(),
+  body('responsibilities').optional().isArray(),
+  body('location').optional().notEmpty().trim(),
+  body('employmentType').optional().isIn(['full-time', 'part-time', 'contract', 'internship']),
+  body('salaryMin').optional().isInt({ min: 0 }),
+  body('salaryMax').optional().isInt({ min: 0 }),
+  body('currency').optional().isLength({ min: 3, max: 3 }),
+  body('status').optional().isIn(['active', 'paused', 'closed', 'draft']),
+  body('remoteAllowed').optional().isBoolean(),
+  body('experienceLevel').optional().isIn(['entry', 'mid', 'senior', 'lead', 'executive']),
+  body('skillsRequired').optional().isArray(),
+  body('benefits').optional().isArray(),
+  body('applicationDeadline').optional().isISO8601()
+], async (req, res) => {
+  try {
+    const errors = validationResult(req);
+    Iif (!errors.isEmpty()) {
+      return res.status(400).json({ errors: errors.array() });
+    }
+ 
+    const { id } = req.params;
+    const {
+      title,
+      description,
+      requirements,
+      responsibilities,
+      location,
+      employmentType,
+      salaryMin,
+      salaryMax,
+      currency,
+      status,
+      remoteAllowed,
+      experienceLevel,
+      skillsRequired,
+      benefits,
+      applicationDeadline
+    } = req.body;
+ 
+    // Check if job exists and user has permission
+    const jobResult = await pool.query(`
+      SELECT j.*, e.user_id as employer_user_id
+      FROM jobs j
+      JOIN employers e ON j.employer_id = e.id
+      WHERE j.id = $1
+    `, [id]);
+ 
+    Iif (jobResult.rows.length === 0) {
+      return res.status(404).json({ error: 'Job not found' });
+    }
+ 
+    const job = jobResult.rows[0];
+ 
+    // Users can only update jobs from their own employer unless they're admin
+    Iif (job.employer_user_id !== req.user.id && req.user.role !== 'admin') {
+      return res.status(403).json({ error: 'Access denied' });
+    }
+ 
+    const updateFields = [];
+    const updateValues = [];
+    let paramCount = 1;
+ 
+    Eif (title) {
+      updateFields.push(`title = $${paramCount}`);
+      updateValues.push(title);
+      paramCount++;
+    }
+    Eif (description) {
+      updateFields.push(`description = $${paramCount}`);
+      updateValues.push(description);
+      paramCount++;
+    }
+    Iif (requirements) {
+      updateFields.push(`requirements = $${paramCount}`);
+      updateValues.push(requirements);
+      paramCount++;
+    }
+    Iif (responsibilities) {
+      updateFields.push(`responsibilities = $${paramCount}`);
+      updateValues.push(responsibilities);
+      paramCount++;
+    }
+    Iif (location) {
+      updateFields.push(`location = $${paramCount}`);
+      updateValues.push(location);
+      paramCount++;
+    }
+    Iif (employmentType) {
+      updateFields.push(`employment_type = $${paramCount}`);
+      updateValues.push(employmentType);
+      paramCount++;
+    }
+    Iif (salaryMin !== undefined) {
+      updateFields.push(`salary_min = $${paramCount}`);
+      updateValues.push(salaryMin);
+      paramCount++;
+    }
+    Iif (salaryMax !== undefined) {
+      updateFields.push(`salary_max = $${paramCount}`);
+      updateValues.push(salaryMax);
+      paramCount++;
+    }
+    Iif (currency) {
+      updateFields.push(`currency = $${paramCount}`);
+      updateValues.push(currency);
+      paramCount++;
+    }
+    Iif (status) {
+      updateFields.push(`status = $${paramCount}`);
+      updateValues.push(status);
+      paramCount++;
+    }
+    Iif (remoteAllowed !== undefined) {
+      updateFields.push(`remote_allowed = $${paramCount}`);
+      updateValues.push(remoteAllowed);
+      paramCount++;
+    }
+    Iif (experienceLevel) {
+      updateFields.push(`experience_level = $${paramCount}`);
+      updateValues.push(experienceLevel);
+      paramCount++;
+    }
+    Iif (skillsRequired) {
+      updateFields.push(`skills_required = $${paramCount}`);
+      updateValues.push(skillsRequired);
+      paramCount++;
+    }
+    Iif (benefits) {
+      updateFields.push(`benefits = $${paramCount}`);
+      updateValues.push(benefits);
+      paramCount++;
+    }
+    Iif (applicationDeadline) {
+      updateFields.push(`application_deadline = $${paramCount}`);
+      updateValues.push(applicationDeadline);
+      paramCount++;
+    }
+ 
+    Iif (updateFields.length === 0) {
+      return res.status(400).json({ error: 'No fields to update' });
+    }
+ 
+    updateValues.push(id);
+    const query = `UPDATE jobs SET ${updateFields.join(', ')}, updated_at = CURRENT_TIMESTAMP WHERE id = $${paramCount} RETURNING *`;
+ 
+    const result = await pool.query(query, updateValues);
+ 
+    res.json({
+      message: 'Job posting updated successfully',
+      job: result.rows[0]
+    });
+  } catch (error) {
+    console.error('Update job error:', error);
+    res.status(500).json({ error: 'Failed to update job posting' });
+  }
+});
+ 
+// Delete job posting
+router.delete('/:id', authenticateToken, async (req, res) => {
+  try {
+    const { id } = req.params;
+ 
+    // Check if job exists and user has permission
+    const jobResult = await pool.query(`
+      SELECT j.*, e.user_id as employer_user_id
+      FROM jobs j
+      JOIN employers e ON j.employer_id = e.id
+      WHERE j.id = $1
+    `, [id]);
+ 
+    Iif (jobResult.rows.length === 0) {
+      return res.status(404).json({ error: 'Job not found' });
+    }
+ 
+    const job = jobResult.rows[0];
+ 
+    // Users can only delete jobs from their own employer unless they're admin
+    Iif (job.employer_user_id !== req.user.id && req.user.role !== 'admin') {
+      return res.status(403).json({ error: 'Access denied' });
+    }
+ 
+    await pool.query('DELETE FROM jobs WHERE id = $1', [id]);
+ 
+    res.json({ message: 'Job posting deleted successfully' });
+  } catch (error) {
+    console.error('Delete job error:', error);
+    res.status(500).json({ error: 'Failed to delete job posting' });
+  }
+});
+ 
+module.exports = router;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/routes/resumes.js.html b/backend/coverage/lcov-report/src/routes/resumes.js.html new file mode 100644 index 0000000..d12fd3e --- /dev/null +++ b/backend/coverage/lcov-report/src/routes/resumes.js.html @@ -0,0 +1,988 @@ + + + + + + Code coverage report for src/routes/resumes.js + + + + + + + + + +
+
+

All files / src/routes resumes.js

+
+ +
+ 64.95% + Statements + 76/117 +
+ + +
+ 34.42% + Branches + 21/61 +
+ + +
+ 88.88% + Functions + 8/9 +
+ + +
+ 64.95% + Lines + 76/117 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +3027x +7x +7x +7x +7x +7x +7x +7x +7x +  +7x +  +  +7x +  +1x +1x +  +  +1x +  +  +1x +1x +1x +  +  +  +7x +  +  +  +  +  +1x +  +  +  +  +  +  +1x +1x +  +  +  +  +  +  +  +7x +1x +1x +  +  +1x +1x +  +  +  +1x +  +  +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +  +  +  +7x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +7x +1x +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +1x +  +  +1x +1x +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +7x +1x +1x +  +1x +  +  +  +  +1x +  +  +  +1x +  +  +1x +1x +  +  +  +1x +  +  +  +  +  +  +  +1x +  +  +  +1x +  +  +  +  +  +  +  +7x +1x +1x +  +  +1x +  +  +  +  +1x +  +  +  +1x +  +  +1x +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +  +1x +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +7x +1x +1x +  +  +1x +  +  +  +  +1x +  +  +  +1x +  +  +1x +  +  +  +  +1x +  +  +  +1x +  +  +1x +1x +  +  +  +1x +  +1x +  +  +  +  +  +  +7x + 
const express = require('express');
+const multer = require('multer');
+const path = require('path');
+const fs = require('fs');
+const { v4: uuidv4 } = require('uuid');
+const sanitize = require('sanitize-filename');
+const pool = require('../database/connection');
+const { authenticateToken, requireRole } = require('../middleware/auth');
+const config = require('../config');
+ 
+const router = express.Router();
+ 
+// Configure multer for file uploads
+const storage = multer.diskStorage({
+  destination: (req, file, cb) => {
+    const uploadDir = path.join(__dirname, '..', '..', config.uploadDir);
+    Iif (!fs.existsSync(uploadDir)) {
+      fs.mkdirSync(uploadDir, { recursive: true });
+    }
+    cb(null, uploadDir);
+  },
+  filename: (req, file, cb) => {
+    const safeOriginal = sanitize(file.originalname);
+    const uniqueName = `${uuidv4()}-${safeOriginal || 'resume'}`;
+    cb(null, uniqueName);
+  }
+});
+ 
+const upload = multer({
+  storage: storage,
+  limits: {
+    fileSize: 10 * 1024 * 1024 // 10MB limit
+  },
+  fileFilter: (req, file, cb) => {
+    const allowedTypes = [
+      'application/pdf',
+      'application/msword',
+      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+      'text/plain'
+    ];
+    
+    if (allowedTypes.includes(file.mimetype)) {
+      cb(null, true);
+    } else E{
+      cb(new Error('Invalid file type. Only PDF, DOC, DOCX, and TXT files are allowed.'));
+    }
+  }
+});
+ 
+// Get all resumes for a candidate
+router.get('/candidate/:candidateId', authenticateToken, async (req, res) => {
+  try {
+    const { candidateId } = req.params;
+ 
+    // Check permissions
+    if (req.user.role === 'candidate') {
+      const candidateResult = await pool.query(
+        'SELECT id FROM candidates WHERE user_id = $1',
+        [req.user.id]
+      );
+      Iif (candidateResult.rows.length === 0 || candidateResult.rows[0].id !== candidateId) {
+        return res.status(403).json({ error: 'Access denied' });
+      }
+    } else Eif (req.user.role !== 'admin' && req.user.role !== 'recruiter' && req.user.role !== 'employer') {
+      return res.status(403).json({ error: 'Access denied' });
+    }
+ 
+    const result = await pool.query(
+      'SELECT * FROM resumes WHERE candidate_id = $1 ORDER BY uploaded_at DESC',
+      [candidateId]
+    );
+ 
+    res.json(result.rows);
+  } catch (error) {
+    console.error('Get resumes error:', error);
+    res.status(500).json({ error: 'Failed to fetch resumes' });
+  }
+});
+ 
+// Get resume by ID
+router.get('/:id', authenticateToken, async (req, res) => {
+  try {
+    const { id } = req.params;
+    
+    const result = await pool.query(
+      'SELECT * FROM resumes WHERE id = $1',
+      [id]
+    );
+ 
+    if (result.rows.length === 0) {
+      return res.status(404).json({ error: 'Resume not found' });
+    }
+ 
+    const resume = result.rows[0];
+ 
+    // Check permissions
+    if (req.user.role === 'candidate') {
+      const candidateResult = await pool.query(
+        'SELECT id FROM candidates WHERE user_id = $1',
+        [req.user.id]
+      );
+      if (candidateResult.rows.length === 0 || candidateResult.rows[0].id !== resume.candidate_id) {
+        return res.status(403).json({ error: 'Access denied' });
+      }
+    } else if (req.user.role !== 'admin' && req.user.role !== 'recruiter' && req.user.role !== 'employer') {
+      return res.status(403).json({ error: 'Access denied' });
+    }
+ 
+    res.json(resume);
+  } catch (error) {
+    console.error('Get resume error:', error);
+    res.status(500).json({ error: 'Failed to fetch resume' });
+  }
+});
+ 
+// Upload resume
+router.post('/upload', authenticateToken, requireRole(['candidate']), upload.single('resume'), async (req, res) => {
+  try {
+    Iif (!req.file) {
+      return res.status(400).json({ error: 'No file uploaded' });
+    }
+ 
+    // Get candidate ID for the current user
+    const candidateResult = await pool.query(
+      'SELECT id FROM candidates WHERE user_id = $1',
+      [req.user.id]
+    );
+ 
+    Iif (candidateResult.rows.length === 0) {
+      return res.status(400).json({ error: 'Candidate profile not found' });
+    }
+ 
+    const candidateId = candidateResult.rows[0].id;
+ 
+    // If this is set as primary, unset other primary resumes
+    Eif (req.body.isPrimary === 'true') {
+      await pool.query(
+        'UPDATE resumes SET is_primary = false WHERE candidate_id = $1',
+        [candidateId]
+      );
+    }
+ 
+    const result = await pool.query(`
+      INSERT INTO resumes (candidate_id, filename, original_name, file_path, file_size, mime_type, is_primary)
+      VALUES ($1, $2, $3, $4, $5, $6, $7)
+      RETURNING *
+    `, [
+      candidateId,
+      req.file.filename,
+      req.file.originalname,
+      req.file.path,
+      req.file.size,
+      req.file.mimetype,
+      req.body.isPrimary === 'true'
+    ]);
+ 
+    res.status(201).json({
+      message: 'Resume uploaded successfully',
+      resume: result.rows[0]
+    });
+  } catch (error) {
+    console.error('Upload resume error:', error);
+    res.status(500).json({ error: 'Failed to upload resume' });
+  }
+});
+ 
+// Download resume
+router.get('/:id/download', authenticateToken, async (req, res) => {
+  try {
+    const { id } = req.params;
+    
+    const result = await pool.query(
+      'SELECT * FROM resumes WHERE id = $1',
+      [id]
+    );
+ 
+    Iif (result.rows.length === 0) {
+      return res.status(404).json({ error: 'Resume not found' });
+    }
+ 
+    const resume = result.rows[0];
+ 
+    // Check permissions
+    if (req.user.role === 'candidate') {
+      const candidateResult = await pool.query(
+        'SELECT id FROM candidates WHERE user_id = $1',
+        [req.user.id]
+      );
+      Iif (candidateResult.rows.length === 0 || candidateResult.rows[0].id !== resume.candidate_id) {
+        return res.status(403).json({ error: 'Access denied' });
+      }
+    } else Eif (req.user.role !== 'admin' && req.user.role !== 'recruiter' && req.user.role !== 'employer') {
+      return res.status(403).json({ error: 'Access denied' });
+    }
+ 
+    // Check if file exists
+    Iif (!fs.existsSync(resume.file_path)) {
+      return res.status(404).json({ error: 'Resume file not found' });
+    }
+ 
+    res.download(resume.file_path, resume.original_name);
+  } catch (error) {
+    console.error('Download resume error:', error);
+    res.status(500).json({ error: 'Failed to download resume' });
+  }
+});
+ 
+// Set primary resume
+router.put('/:id/primary', authenticateToken, requireRole(['candidate']), async (req, res) => {
+  try {
+    const { id } = req.params;
+ 
+    // Get candidate ID for the current user
+    const candidateResult = await pool.query(
+      'SELECT id FROM candidates WHERE user_id = $1',
+      [req.user.id]
+    );
+ 
+    Iif (candidateResult.rows.length === 0) {
+      return res.status(400).json({ error: 'Candidate profile not found' });
+    }
+ 
+    const candidateId = candidateResult.rows[0].id;
+ 
+    // Check if resume exists and belongs to the candidate
+    const resumeResult = await pool.query(
+      'SELECT id FROM resumes WHERE id = $1 AND candidate_id = $2',
+      [id, candidateId]
+    );
+ 
+    Iif (resumeResult.rows.length === 0) {
+      return res.status(404).json({ error: 'Resume not found' });
+    }
+ 
+    // Unset other primary resumes
+    await pool.query(
+      'UPDATE resumes SET is_primary = false WHERE candidate_id = $1',
+      [candidateId]
+    );
+ 
+    // Set this resume as primary
+    const result = await pool.query(
+      'UPDATE resumes SET is_primary = true WHERE id = $1 RETURNING *',
+      [id]
+    );
+ 
+    res.json({
+      message: 'Primary resume updated successfully',
+      resume: result.rows[0]
+    });
+  } catch (error) {
+    console.error('Set primary resume error:', error);
+    res.status(500).json({ error: 'Failed to set primary resume' });
+  }
+});
+ 
+// Delete resume
+router.delete('/:id', authenticateToken, requireRole(['candidate']), async (req, res) => {
+  try {
+    const { id } = req.params;
+ 
+    // Get candidate ID for the current user
+    const candidateResult = await pool.query(
+      'SELECT id FROM candidates WHERE user_id = $1',
+      [req.user.id]
+    );
+ 
+    Iif (candidateResult.rows.length === 0) {
+      return res.status(400).json({ error: 'Candidate profile not found' });
+    }
+ 
+    const candidateId = candidateResult.rows[0].id;
+ 
+    // Check if resume exists and belongs to the candidate
+    const resumeResult = await pool.query(
+      'SELECT * FROM resumes WHERE id = $1 AND candidate_id = $2',
+      [id, candidateId]
+    );
+ 
+    Iif (resumeResult.rows.length === 0) {
+      return res.status(404).json({ error: 'Resume not found' });
+    }
+ 
+    const resume = resumeResult.rows[0];
+ 
+    // Delete file from filesystem
+    Eif (fs.existsSync(resume.file_path)) {
+      fs.unlinkSync(resume.file_path);
+    }
+ 
+    // Delete from database
+    await pool.query('DELETE FROM resumes WHERE id = $1', [id]);
+ 
+    res.json({ message: 'Resume deleted successfully' });
+  } catch (error) {
+    console.error('Delete resume error:', error);
+    res.status(500).json({ error: 'Failed to delete resume' });
+  }
+});
+ 
+module.exports = router;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/routes/users.js.html b/backend/coverage/lcov-report/src/routes/users.js.html new file mode 100644 index 0000000..bc1fe82 --- /dev/null +++ b/backend/coverage/lcov-report/src/routes/users.js.html @@ -0,0 +1,580 @@ + + + + + + Code coverage report for src/routes/users.js + + + + + + + + + +
+
+

All files / src/routes users.js

+
+ +
+ 58.44% + Statements + 45/77 +
+ + +
+ 35.71% + Branches + 10/28 +
+ + +
+ 80% + Functions + 4/5 +
+ + +
+ 58.44% + Lines + 45/77 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +1667x +7x +7x +7x +  +7x +  +  +7x +1x +1x +  +  +1x +  +  +  +  +  +  +  +7x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +7x +  +  +  +  +1x +1x +1x +  +  +  +1x +1x +  +  +1x +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +1x +1x +1x +  +1x +1x +1x +1x +  +1x +  +  +  +  +1x +  +  +  +  +  +1x +  +  +  +1x +1x +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +7x +1x +1x +  +1x +  +  +  +  +1x +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +7x +1x +1x +  +1x +  +  +  +  +1x +  +  +  +1x +  +  +  +  +  +  +  +  +  +7x + 
const express = require('express');
+const { body, validationResult } = require('express-validator');
+const pool = require('../database/connection');
+const { authenticateToken, requireRole } = require('../middleware/auth');
+ 
+const router = express.Router();
+ 
+// Get all users (admin only)
+router.get('/', authenticateToken, requireRole(['admin']), async (req, res) => {
+  try {
+    const result = await pool.query(
+      'SELECT id, email, first_name, last_name, role, is_active, created_at FROM users ORDER BY created_at DESC'
+    );
+    res.json(result.rows);
+  } catch (error) {
+    console.error('Get users error:', error);
+    res.status(500).json({ error: 'Failed to fetch users' });
+  }
+});
+ 
+// Get user by ID
+router.get('/:id', authenticateToken, async (req, res) => {
+  try {
+    const { id } = req.params;
+    
+    // Users can only view their own profile unless they're admin
+    if (req.user.id !== id && req.user.role !== 'admin') {
+      return res.status(403).json({ error: 'Access denied' });
+    }
+ 
+    const result = await pool.query(
+      'SELECT id, email, first_name, last_name, role, is_active, created_at FROM users WHERE id = $1',
+      [id]
+    );
+ 
+    if (result.rows.length === 0) {
+      return res.status(404).json({ error: 'User not found' });
+    }
+ 
+    res.json(result.rows[0]);
+  } catch (error) {
+    console.error('Get user error:', error);
+    res.status(500).json({ error: 'Failed to fetch user' });
+  }
+});
+ 
+// Update user profile
+router.put('/:id', authenticateToken, [
+  body('firstName').optional().notEmpty().trim(),
+  body('lastName').optional().notEmpty().trim(),
+  body('email').optional().isEmail().normalizeEmail()
+], async (req, res) => {
+  try {
+    const errors = validationResult(req);
+    Iif (!errors.isEmpty()) {
+      return res.status(400).json({ errors: errors.array() });
+    }
+ 
+    const { id } = req.params;
+    const { firstName, lastName, email } = req.body;
+ 
+    // Users can only update their own profile unless they're admin
+    Iif (req.user.id !== id && req.user.role !== 'admin') {
+      return res.status(403).json({ error: 'Access denied' });
+    }
+ 
+    // Check if email is already taken by another user
+    Iif (email) {
+      const existingUser = await pool.query(
+        'SELECT id FROM users WHERE email = $1 AND id != $2',
+        [email, id]
+      );
+      if (existingUser.rows.length > 0) {
+        return res.status(400).json({ error: 'Email already in use' });
+      }
+    }
+ 
+    const updateFields = [];
+    const updateValues = [];
+    let paramCount = 1;
+ 
+    Eif (firstName) {
+      updateFields.push(`first_name = $${paramCount}`);
+      updateValues.push(firstName);
+      paramCount++;
+    }
+    Iif (lastName) {
+      updateFields.push(`last_name = $${paramCount}`);
+      updateValues.push(lastName);
+      paramCount++;
+    }
+    Iif (email) {
+      updateFields.push(`email = $${paramCount}`);
+      updateValues.push(email);
+      paramCount++;
+    }
+ 
+    Iif (updateFields.length === 0) {
+      return res.status(400).json({ error: 'No fields to update' });
+    }
+ 
+    updateValues.push(id);
+    const query = `UPDATE users SET ${updateFields.join(', ')}, updated_at = CURRENT_TIMESTAMP WHERE id = $${paramCount} RETURNING id, email, first_name, last_name, role, is_active, updated_at`;
+ 
+    const result = await pool.query(query, updateValues);
+ 
+    res.json({
+      message: 'User updated successfully',
+      user: result.rows[0]
+    });
+  } catch (error) {
+    console.error('Update user error:', error);
+    res.status(500).json({ error: 'Failed to update user' });
+  }
+});
+ 
+// Deactivate user (admin only)
+router.put('/:id/deactivate', authenticateToken, requireRole(['admin']), async (req, res) => {
+  try {
+    const { id } = req.params;
+ 
+    const result = await pool.query(
+      'UPDATE users SET is_active = false, updated_at = CURRENT_TIMESTAMP WHERE id = $1 RETURNING id, email, first_name, last_name, role, is_active',
+      [id]
+    );
+ 
+    Iif (result.rows.length === 0) {
+      return res.status(404).json({ error: 'User not found' });
+    }
+ 
+    res.json({
+      message: 'User deactivated successfully',
+      user: result.rows[0]
+    });
+  } catch (error) {
+    console.error('Deactivate user error:', error);
+    res.status(500).json({ error: 'Failed to deactivate user' });
+  }
+});
+ 
+// Activate user (admin only)
+router.put('/:id/activate', authenticateToken, requireRole(['admin']), async (req, res) => {
+  try {
+    const { id } = req.params;
+ 
+    const result = await pool.query(
+      'UPDATE users SET is_active = true, updated_at = CURRENT_TIMESTAMP WHERE id = $1 RETURNING id, email, first_name, last_name, role, is_active',
+      [id]
+    );
+ 
+    Iif (result.rows.length === 0) {
+      return res.status(404).json({ error: 'User not found' });
+    }
+ 
+    res.json({
+      message: 'User activated successfully',
+      user: result.rows[0]
+    });
+  } catch (error) {
+    console.error('Activate user error:', error);
+    res.status(500).json({ error: 'Failed to activate user' });
+  }
+});
+ 
+module.exports = router;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/server.js.html b/backend/coverage/lcov-report/src/server.js.html new file mode 100644 index 0000000..a4668ad --- /dev/null +++ b/backend/coverage/lcov-report/src/server.js.html @@ -0,0 +1,271 @@ + + + + + + Code coverage report for src/server.js + + + + + + + + + +
+
+

All files / src server.js

+
+ +
+ 86.84% + Statements + 33/38 +
+ + +
+ 37.5% + Branches + 3/8 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 86.84% + Lines + 33/38 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +637x +7x +7x +7x +7x +7x +  +7x +7x +7x +7x +7x +7x +7x +  +7x +  +7x +  +  +  +  +  +  +7x +  +7x +  +  +  +7x +7x +7x +7x +7x +  +7x +7x +7x +7x +7x +7x +7x +  +7x +  +  +  +  +7x +  +  +  +  +  +  +  +7x +  +  +  +7x + 
const express = require('express');
+const cors = require('cors');
+const helmet = require('helmet');
+const morgan = require('morgan');
+const rateLimit = require('express-rate-limit');
+const config = require('./config');
+ 
+const authRoutes = require('./routes/auth');
+const userRoutes = require('./routes/users');
+const employerRoutes = require('./routes/employers');
+const candidateRoutes = require('./routes/candidates');
+const jobRoutes = require('./routes/jobs');
+const applicationRoutes = require('./routes/applications');
+const resumeRoutes = require('./routes/resumes');
+ 
+const app = express();
+ 
+const authLimiter = rateLimit({
+  windowMs: config.rateLimit.windowMs,
+  max: config.rateLimit.max,
+  standardHeaders: true,
+  legacyHeaders: false
+});
+ 
+app.disable('x-powered-by');
+ 
+const corsOrigins = config.corsOrigin === '*'
+  ? undefined
+  : config.corsOrigin.split(',').map((origin) => origin.trim());
+ 
+app.use(helmet());
+app.use(cors(corsOrigins ? { origin: corsOrigins, credentials: true } : {}));
+app.use(morgan(config.env === 'production' ? 'combined' : 'dev'));
+app.use(express.json({ limit: '10mb' }));
+app.use(express.urlencoded({ extended: true }));
+ 
+app.use('/api/auth', authLimiter, authRoutes);
+app.use('/api/users', userRoutes);
+app.use('/api/employers', employerRoutes);
+app.use('/api/candidates', candidateRoutes);
+app.use('/api/jobs', jobRoutes);
+app.use('/api/applications', applicationRoutes);
+app.use('/api/resumes', resumeRoutes);
+ 
+app.get('/api/health', (req, res) => {
+  res.json({ status: 'OK', timestamp: new Date().toISOString() });
+});
+ 
+// eslint-disable-next-line no-unused-vars
+app.use((err, req, res, next) => {
+  console.error(err.stack);
+  res.status(500).json({
+    error: 'Something went wrong!',
+    message: config.env === 'development' ? err.message : 'Internal server error'
+  });
+});
+ 
+app.use('*', (req, res) => {
+  res.status(404).json({ error: 'Route not found' });
+});
+ 
+module.exports = app;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/tests/index.html b/backend/coverage/lcov-report/src/tests/index.html new file mode 100644 index 0000000..9881e24 --- /dev/null +++ b/backend/coverage/lcov-report/src/tests/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/tests + + + + + + + + + +
+
+

All files src/tests

+
+ +
+ 86.11% + Statements + 31/36 +
+ + +
+ 86.66% + Branches + 26/30 +
+ + +
+ 100% + Functions + 6/6 +
+ + +
+ 85.71% + Lines + 30/35 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
utils.js +
+
86.11%31/3686.66%26/30100%6/685.71%30/35
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov-report/src/tests/utils.js.html b/backend/coverage/lcov-report/src/tests/utils.js.html new file mode 100644 index 0000000..8dbe960 --- /dev/null +++ b/backend/coverage/lcov-report/src/tests/utils.js.html @@ -0,0 +1,421 @@ + + + + + + Code coverage report for src/tests/utils.js + + + + + + + + + +
+
+

All files / src/tests utils.js

+
+ +
+ 86.11% + Statements + 31/36 +
+ + +
+ 86.66% + Branches + 26/30 +
+ + +
+ 100% + Functions + 6/6 +
+ + +
+ 85.71% + Lines + 30/35 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +1137x +7x +7x +7x +7x +7x +7x +7x +  +16x +  +7x +  +  +15x +  +  +  +  +  +  +  +  +15x +  +  +  +15x +  +  +  +  +  +  +  +  +  +  +11x +  +  +  +  +  +  +  +  +  +  +11x +  +  +  +  +11x +  +  +  +1x +1x +1x +  +1x +  +  +  +  +1x +  +  +  +1x +  +  +  +  +  +  +  +  +24x +24x +  +  +24x +  +  +  +23x +23x +  +  +23x +23x +  +  +  +  +  +  +  +  +  +7x +  +  +  +  +  +  +  +  + 
const { randomUUID } = require('crypto');
+const path = require('path');
+const fs = require('fs');
+const bcrypt = require('bcryptjs');
+const request = require('supertest');
+const app = require('../server');
+const config = require('../config');
+const pool = require('../database/connection');
+ 
+const uniqueEmail = (prefix = 'user') => `${prefix}-${randomUUID()}@example.com`;
+ 
+const defaultPassword = 'Password123!';
+ 
+async function registerUser(role = 'candidate', overrides = {}) {
+  const userData = {
+    firstName: overrides.firstName || 'Test',
+    lastName: overrides.lastName || 'User',
+    email: overrides.email || uniqueEmail(role),
+    password: overrides.password || defaultPassword,
+    role,
+    ...overrides
+  };
+ 
+  const response = await request(app)
+    .post('/api/auth/register')
+    .send(userData);
+ 
+  return {
+    token: response.body.token,
+    user: response.body.user,
+    credentials: {
+      email: userData.email,
+      password: userData.password
+    }
+  };
+}
+ 
+async function createEmployerProfile(token, overrides = {}) {
+  const employerData = {
+    companyName: overrides.companyName || `Company ${randomUUID().slice(0, 8)}`,
+    industry: overrides.industry || 'Technology',
+    companySize: overrides.companySize || '11-50',
+    website: overrides.website || 'https://example.com',
+    description: overrides.description || 'Test company description',
+    address: overrides.address || '123 Test St',
+    phone: overrides.phone || '+1-555-0000',
+    ...overrides
+  };
+ 
+  const response = await request(app)
+    .post('/api/employers')
+    .set('Authorization', `Bearer ${token}`)
+    .send(employerData);
+ 
+  return response.body.employer;
+}
+ 
+async function createAdminUser() {
+  const email = uniqueEmail('admin');
+  const password = defaultPassword;
+  const passwordHash = await bcrypt.hash(password, 10);
+ 
+  const result = await pool.query(
+    'INSERT INTO users (email, password_hash, first_name, last_name, role) VALUES ($1, $2, $3, $4, $5) RETURNING id',
+    [email, passwordHash, 'Admin', 'User', 'admin']
+  );
+ 
+  const loginResponse = await request(app)
+    .post('/api/auth/login')
+    .send({ email, password });
+ 
+  return {
+    token: loginResponse.body.token,
+    user: loginResponse.body.user,
+    userId: result.rows[0].id,
+    credentials: { email, password }
+  };
+}
+ 
+async function ensureUploadDir() {
+  const uploadDir = path.join(__dirname, '..', '..', config.uploadDir);
+  Iif (!fs.existsSync(uploadDir)) {
+    fs.mkdirSync(uploadDir, { recursive: true });
+  }
+  return uploadDir;
+}
+ 
+async function cleanupUploads() {
+  const uploadDir = await ensureUploadDir();
+  Iif (!fs.existsSync(uploadDir)) {
+    return;
+  }
+  const entries = fs.readdirSync(uploadDir);
+  for (const entry of entries) {
+    const filePath = path.join(uploadDir, entry);
+    try {
+      fs.unlinkSync(filePath);
+    } catch (error) {
+      // Ignore deletion errors in tests
+    }
+  }
+}
+ 
+module.exports = {
+  registerUser,
+  createEmployerProfile,
+  createAdminUser,
+  uniqueEmail,
+  defaultPassword,
+  ensureUploadDir,
+  cleanupUploads
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/coverage/lcov.info b/backend/coverage/lcov.info new file mode 100644 index 0000000..8646a48 --- /dev/null +++ b/backend/coverage/lcov.info @@ -0,0 +1,1768 @@ +TN: +SF:src/server.js +FN:29,(anonymous_0) +FN:45,(anonymous_1) +FN:50,(anonymous_2) +FN:58,(anonymous_3) +FNF:4 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +DA:1,7 +DA:2,7 +DA:3,7 +DA:4,7 +DA:5,7 +DA:6,7 +DA:8,7 +DA:9,7 +DA:10,7 +DA:11,7 +DA:12,7 +DA:13,7 +DA:14,7 +DA:16,7 +DA:18,7 +DA:25,7 +DA:27,7 +DA:29,0 +DA:31,7 +DA:32,7 +DA:33,7 +DA:34,7 +DA:35,7 +DA:37,7 +DA:38,7 +DA:39,7 +DA:40,7 +DA:41,7 +DA:42,7 +DA:43,7 +DA:45,7 +DA:46,0 +DA:50,7 +DA:51,0 +DA:52,0 +DA:58,7 +DA:59,0 +DA:62,7 +LF:38 +LH:33 +BRDA:27,0,0,7 +BRDA:27,0,1,0 +BRDA:32,1,0,0 +BRDA:32,1,1,7 +BRDA:33,2,0,0 +BRDA:33,2,1,7 +BRDA:54,3,0,0 +BRDA:54,3,1,0 +BRF:8 +BRH:3 +end_of_record +TN: +SF:src/config/index.js +FN:5,(anonymous_0) +FN:10,(anonymous_1) +FN:46,(anonymous_2) +FNF:3 +FNH:3 +FNDA:21,(anonymous_0) +FNDA:7,(anonymous_1) +FNDA:14,(anonymous_2) +DA:1,7 +DA:3,7 +DA:5,7 +DA:6,21 +DA:7,21 +DA:10,7 +DA:11,7 +DA:12,0 +DA:21,7 +DA:23,7 +DA:24,0 +DA:27,7 +DA:30,7 +DA:45,7 +DA:46,14 +DA:48,7 +DA:49,0 +DA:52,7 +LF:18 +LH:15 +BRDA:7,0,0,0 +BRDA:7,0,1,21 +BRDA:11,1,0,0 +BRDA:11,1,1,7 +BRDA:16,2,0,0 +BRDA:18,3,0,0 +BRDA:19,4,0,0 +BRDA:20,5,0,0 +BRDA:23,6,0,0 +BRDA:23,6,1,7 +BRDA:31,7,0,7 +BRDA:31,7,1,0 +BRDA:32,8,0,7 +BRDA:32,8,1,7 +BRDA:32,8,2,7 +BRDA:33,9,0,7 +BRDA:33,9,1,7 +BRDA:36,10,0,7 +BRDA:36,10,1,7 +BRDA:37,11,0,7 +BRDA:37,11,1,7 +BRDA:38,12,0,7 +BRDA:38,12,1,7 +BRDA:48,13,0,0 +BRDA:48,13,1,7 +BRF:25 +BRH:16 +end_of_record +TN: +SF:src/database/connection.js +FN:10,(anonymous_0) +FN:16,(anonymous_1) +FNF:2 +FNH:1 +FNDA:7,(anonymous_0) +FNDA:0,(anonymous_1) +DA:1,7 +DA:2,7 +DA:4,7 +DA:10,7 +DA:11,7 +DA:12,7 +DA:16,7 +DA:17,0 +DA:18,0 +DA:20,0 +DA:23,7 +LF:11 +LH:8 +BRDA:6,0,0,0 +BRDA:6,0,1,7 +BRDA:11,1,0,7 +BRDA:11,1,1,0 +BRDA:17,2,0,0 +BRDA:17,2,1,0 +BRDA:17,3,0,0 +BRDA:17,3,1,0 +BRF:8 +BRH:2 +end_of_record +TN: +SF:src/middleware/auth.js +FN:5,(anonymous_0) +FN:38,(anonymous_1) +FN:39,(anonymous_2) +FNF:3 +FNH:3 +FNDA:53,(anonymous_0) +FNDA:70,(anonymous_1) +FNDA:31,(anonymous_2) +DA:1,7 +DA:2,7 +DA:3,7 +DA:5,7 +DA:6,53 +DA:7,53 +DA:9,53 +DA:10,1 +DA:13,52 +DA:14,52 +DA:17,51 +DA:22,51 +DA:23,0 +DA:26,51 +DA:27,51 +DA:28,0 +DA:31,51 +DA:32,51 +DA:34,1 +DA:38,7 +DA:39,70 +DA:40,31 +DA:41,0 +DA:44,31 +DA:45,0 +DA:48,31 +DA:52,7 +LF:27 +LH:23 +BRDA:7,0,0,53 +BRDA:7,0,1,52 +BRDA:9,1,0,1 +BRDA:9,1,1,52 +BRDA:22,2,0,0 +BRDA:22,2,1,51 +BRDA:27,3,0,0 +BRDA:27,3,1,51 +BRDA:40,4,0,0 +BRDA:40,4,1,31 +BRDA:44,5,0,0 +BRDA:44,5,1,31 +BRF:12 +BRH:8 +end_of_record +TN: +SF:src/routes/applications.js +FN:9,(anonymous_0) +FN:186,(anonymous_1) +FN:245,(anonymous_2) +FN:309,(anonymous_3) +FN:373,(anonymous_4) +FNF:5 +FNH:5 +FNDA:1,(anonymous_0) +FNDA:1,(anonymous_1) +FNDA:1,(anonymous_2) +FNDA:1,(anonymous_3) +FNDA:1,(anonymous_4) +DA:1,7 +DA:2,7 +DA:3,7 +DA:4,7 +DA:6,7 +DA:9,7 +DA:10,1 +DA:18,1 +DA:20,1 +DA:35,1 +DA:36,1 +DA:37,1 +DA:39,1 +DA:40,0 +DA:41,0 +DA:42,0 +DA:45,1 +DA:46,0 +DA:47,0 +DA:48,0 +DA:51,1 +DA:52,0 +DA:53,0 +DA:54,0 +DA:57,1 +DA:58,0 +DA:59,0 +DA:60,0 +DA:64,1 +DA:66,1 +DA:70,1 +DA:71,1 +DA:72,1 +DA:73,1 +DA:75,0 +DA:77,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:84,0 +DA:88,1 +DA:89,1 +DA:92,1 +DA:95,1 +DA:96,1 +DA:97,1 +DA:98,1 +DA:99,1 +DA:100,1 +DA:101,1 +DA:103,1 +DA:106,1 +DA:113,1 +DA:114,1 +DA:115,1 +DA:117,1 +DA:118,0 +DA:119,0 +DA:120,0 +DA:123,1 +DA:124,0 +DA:125,0 +DA:126,0 +DA:129,1 +DA:130,0 +DA:131,0 +DA:132,0 +DA:135,1 +DA:136,0 +DA:137,0 +DA:138,0 +DA:142,1 +DA:143,1 +DA:147,1 +DA:148,1 +DA:149,1 +DA:150,1 +DA:152,0 +DA:153,0 +DA:157,0 +DA:158,0 +DA:159,0 +DA:160,0 +DA:164,1 +DA:165,1 +DA:168,1 +DA:170,1 +DA:180,0 +DA:181,0 +DA:186,7 +DA:187,1 +DA:188,1 +DA:190,1 +DA:208,1 +DA:209,0 +DA:212,1 +DA:215,1 +DA:216,0 +DA:220,0 +DA:221,0 +DA:223,1 +DA:224,1 +DA:228,1 +DA:229,0 +DA:233,1 +DA:235,0 +DA:236,0 +DA:241,7 +DA:246,1 +DA:247,1 +DA:248,1 +DA:249,0 +DA:252,1 +DA:255,1 +DA:260,1 +DA:261,0 +DA:264,1 +DA:267,1 +DA:272,1 +DA:273,0 +DA:276,1 +DA:277,0 +DA:281,1 +DA:286,1 +DA:287,0 +DA:290,1 +DA:296,1 +DA:301,0 +DA:302,0 +DA:307,7 +DA:310,1 +DA:311,1 +DA:312,1 +DA:313,0 +DA:316,1 +DA:317,1 +DA:320,1 +DA:328,1 +DA:329,0 +DA:332,1 +DA:335,1 +DA:336,0 +DA:340,0 +DA:341,0 +DA:344,0 +DA:345,0 +DA:347,1 +DA:348,1 +DA:349,0 +DA:351,0 +DA:352,0 +DA:355,1 +DA:360,1 +DA:365,0 +DA:366,0 +DA:371,7 +DA:374,1 +DA:375,1 +DA:376,1 +DA:377,0 +DA:380,1 +DA:381,1 +DA:384,1 +DA:392,1 +DA:393,0 +DA:396,1 +DA:399,1 +DA:400,0 +DA:401,1 +DA:402,1 +DA:403,0 +DA:405,0 +DA:406,0 +DA:409,1 +DA:414,1 +DA:419,0 +DA:420,0 +DA:424,7 +LF:178 +LH:106 +BRDA:16,0,0,1 +BRDA:17,1,0,1 +BRDA:39,2,0,0 +BRDA:39,2,1,1 +BRDA:45,3,0,0 +BRDA:45,3,1,1 +BRDA:51,4,0,0 +BRDA:51,4,1,1 +BRDA:57,5,0,0 +BRDA:57,5,1,1 +BRDA:64,6,0,1 +BRDA:64,6,1,0 +BRDA:70,7,0,1 +BRDA:70,7,1,0 +BRDA:75,8,0,0 +BRDA:75,8,1,0 +BRDA:81,9,0,0 +BRDA:81,9,1,0 +BRDA:88,10,0,1 +BRDA:88,10,1,0 +BRDA:117,11,0,0 +BRDA:117,11,1,1 +BRDA:123,12,0,0 +BRDA:123,12,1,1 +BRDA:129,13,0,0 +BRDA:129,13,1,1 +BRDA:135,14,0,0 +BRDA:135,14,1,1 +BRDA:142,15,0,1 +BRDA:142,15,1,0 +BRDA:147,16,0,1 +BRDA:147,16,1,0 +BRDA:152,17,0,0 +BRDA:152,17,1,0 +BRDA:157,18,0,0 +BRDA:157,18,1,0 +BRDA:164,19,0,1 +BRDA:164,19,1,0 +BRDA:208,20,0,0 +BRDA:208,20,1,1 +BRDA:215,21,0,0 +BRDA:215,21,1,1 +BRDA:220,22,0,0 +BRDA:220,22,1,0 +BRDA:220,23,0,0 +BRDA:220,23,1,0 +BRDA:223,24,0,1 +BRDA:223,24,1,0 +BRDA:228,25,0,0 +BRDA:228,25,1,1 +BRDA:228,26,0,1 +BRDA:228,26,1,1 +BRDA:248,27,0,0 +BRDA:248,27,1,1 +BRDA:260,28,0,0 +BRDA:260,28,1,1 +BRDA:272,29,0,0 +BRDA:272,29,1,1 +BRDA:276,30,0,0 +BRDA:276,30,1,1 +BRDA:286,31,0,0 +BRDA:286,31,1,1 +BRDA:312,32,0,0 +BRDA:312,32,1,1 +BRDA:328,33,0,0 +BRDA:328,33,1,1 +BRDA:335,34,0,0 +BRDA:335,34,1,1 +BRDA:340,35,0,0 +BRDA:340,35,1,0 +BRDA:340,36,0,0 +BRDA:340,36,1,0 +BRDA:344,37,0,0 +BRDA:344,37,1,0 +BRDA:347,38,0,1 +BRDA:347,38,1,0 +BRDA:348,39,0,0 +BRDA:348,39,1,1 +BRDA:351,40,0,0 +BRDA:351,40,1,0 +BRDA:351,41,0,0 +BRDA:351,41,1,0 +BRDA:376,42,0,0 +BRDA:376,42,1,1 +BRDA:392,43,0,0 +BRDA:392,43,1,1 +BRDA:399,44,0,0 +BRDA:399,44,1,1 +BRDA:401,45,0,1 +BRDA:401,45,1,0 +BRDA:402,46,0,0 +BRDA:402,46,1,1 +BRDA:405,47,0,0 +BRDA:405,47,1,0 +BRDA:405,48,0,0 +BRDA:405,48,1,0 +BRF:96 +BRH:37 +end_of_record +TN: +SF:src/routes/auth.js +FN:18,(anonymous_0) +FN:76,(anonymous_1) +FN:132,(anonymous_2) +FN:150,(anonymous_3) +FNF:4 +FNH:3 +FNDA:25,(anonymous_0) +FNDA:4,(anonymous_1) +FNDA:1,(anonymous_2) +FNDA:0,(anonymous_3) +DA:1,7 +DA:2,7 +DA:3,7 +DA:4,7 +DA:5,7 +DA:6,7 +DA:7,7 +DA:9,7 +DA:12,7 +DA:19,25 +DA:20,25 +DA:21,25 +DA:22,1 +DA:25,24 +DA:28,24 +DA:33,24 +DA:34,1 +DA:38,23 +DA:41,23 +DA:46,23 +DA:49,23 +DA:55,23 +DA:67,0 +DA:68,0 +DA:73,7 +DA:77,4 +DA:78,4 +DA:79,4 +DA:80,0 +DA:83,4 +DA:86,4 +DA:91,4 +DA:92,1 +DA:95,3 +DA:97,3 +DA:98,0 +DA:102,3 +DA:103,3 +DA:104,1 +DA:108,2 +DA:114,2 +DA:126,0 +DA:127,0 +DA:132,7 +DA:133,1 +DA:134,1 +DA:144,0 +DA:145,0 +DA:150,7 +DA:151,0 +DA:154,7 +LF:51 +LH:42 +BRDA:21,0,0,1 +BRDA:21,0,1,24 +BRDA:33,1,0,1 +BRDA:33,1,1,23 +BRDA:79,2,0,0 +BRDA:79,2,1,4 +BRDA:91,3,0,1 +BRDA:91,3,1,3 +BRDA:97,4,0,0 +BRDA:97,4,1,3 +BRDA:103,5,0,1 +BRDA:103,5,1,2 +BRF:12 +BRH:10 +end_of_record +TN: +SF:src/routes/candidates.js +FN:9,(anonymous_0) +FN:23,(anonymous_1) +FN:69,(anonymous_2) +FN:109,(anonymous_3) +FN:143,(anonymous_4) +FN:201,(anonymous_5) +FN:312,(anonymous_6) +FNF:7 +FNH:6 +FNDA:1,(anonymous_0) +FNDA:1,(anonymous_1) +FNDA:1,(anonymous_2) +FNDA:1,(anonymous_3) +FNDA:3,(anonymous_4) +FNDA:1,(anonymous_5) +FNDA:0,(anonymous_6) +DA:1,7 +DA:2,7 +DA:3,7 +DA:4,7 +DA:6,7 +DA:9,7 +DA:10,1 +DA:11,1 +DA:13,1 +DA:18,1 +DA:19,1 +DA:20,1 +DA:22,1 +DA:23,1 +DA:24,1 +DA:25,1 +DA:26,1 +DA:29,1 +DA:30,0 +DA:31,0 +DA:32,0 +DA:35,1 +DA:36,1 +DA:37,1 +DA:38,1 +DA:41,1 +DA:42,1 +DA:45,1 +DA:48,1 +DA:49,1 +DA:50,1 +DA:51,1 +DA:52,1 +DA:53,1 +DA:54,1 +DA:56,1 +DA:59,1 +DA:64,1 +DA:65,1 +DA:66,1 +DA:68,1 +DA:69,1 +DA:70,1 +DA:71,1 +DA:72,1 +DA:75,1 +DA:76,0 +DA:77,0 +DA:78,0 +DA:81,1 +DA:82,1 +DA:83,1 +DA:84,1 +DA:87,1 +DA:88,1 +DA:91,1 +DA:93,1 +DA:103,0 +DA:104,0 +DA:109,7 +DA:110,1 +DA:111,1 +DA:113,1 +DA:120,1 +DA:121,0 +DA:124,1 +DA:126,0 +DA:127,0 +DA:132,7 +DA:144,3 +DA:145,3 +DA:146,3 +DA:147,0 +DA:161,3 +DA:164,3 +DA:169,3 +DA:170,0 +DA:173,3 +DA:179,3 +DA:184,0 +DA:185,0 +DA:190,7 +DA:202,1 +DA:203,1 +DA:204,1 +DA:205,0 +DA:208,1 +DA:220,1 +DA:223,1 +DA:228,1 +DA:229,0 +DA:233,1 +DA:234,0 +DA:237,1 +DA:238,1 +DA:239,1 +DA:241,1 +DA:242,0 +DA:243,0 +DA:244,0 +DA:246,1 +DA:247,0 +DA:248,0 +DA:249,0 +DA:251,1 +DA:252,0 +DA:253,0 +DA:254,0 +DA:256,1 +DA:257,0 +DA:258,0 +DA:259,0 +DA:261,1 +DA:262,0 +DA:263,0 +DA:264,0 +DA:266,1 +DA:267,0 +DA:268,0 +DA:269,0 +DA:271,1 +DA:272,0 +DA:273,0 +DA:274,0 +DA:276,1 +DA:277,0 +DA:278,0 +DA:279,0 +DA:281,1 +DA:282,1 +DA:283,1 +DA:284,1 +DA:286,1 +DA:287,0 +DA:288,0 +DA:289,0 +DA:292,1 +DA:293,0 +DA:296,1 +DA:297,1 +DA:299,1 +DA:301,1 +DA:306,0 +DA:307,0 +DA:312,7 +DA:313,0 +DA:314,0 +DA:315,0 +DA:317,0 +DA:324,0 +DA:325,0 +DA:327,0 +DA:328,0 +DA:329,0 +DA:330,0 +DA:333,0 +DA:336,0 +DA:337,0 +DA:338,0 +DA:339,0 +DA:340,0 +DA:341,0 +DA:342,0 +DA:344,0 +DA:347,0 +DA:352,0 +DA:353,0 +DA:354,0 +DA:355,0 +DA:357,0 +DA:359,0 +DA:369,0 +DA:370,0 +DA:374,7 +LF:174 +LH:98 +BRDA:11,0,0,1 +BRDA:11,1,0,1 +BRDA:22,2,0,1 +BRDA:22,2,1,0 +BRDA:29,3,0,0 +BRDA:29,3,1,1 +BRDA:35,4,0,1 +BRDA:35,4,1,0 +BRDA:41,5,0,1 +BRDA:41,5,1,0 +BRDA:68,6,0,1 +BRDA:68,6,1,0 +BRDA:75,7,0,0 +BRDA:75,7,1,1 +BRDA:81,8,0,1 +BRDA:81,8,1,0 +BRDA:87,9,0,1 +BRDA:87,9,1,0 +BRDA:120,10,0,0 +BRDA:120,10,1,1 +BRDA:146,11,0,0 +BRDA:146,11,1,3 +BRDA:169,12,0,0 +BRDA:169,12,1,3 +BRDA:204,13,0,0 +BRDA:204,13,1,1 +BRDA:228,14,0,0 +BRDA:228,14,1,1 +BRDA:233,15,0,0 +BRDA:233,15,1,1 +BRDA:233,16,0,1 +BRDA:233,16,1,0 +BRDA:241,17,0,0 +BRDA:241,17,1,1 +BRDA:246,18,0,0 +BRDA:246,18,1,1 +BRDA:251,19,0,0 +BRDA:251,19,1,1 +BRDA:256,20,0,0 +BRDA:256,20,1,1 +BRDA:261,21,0,0 +BRDA:261,21,1,1 +BRDA:266,22,0,0 +BRDA:266,22,1,1 +BRDA:271,23,0,0 +BRDA:271,23,1,1 +BRDA:276,24,0,0 +BRDA:276,24,1,1 +BRDA:281,25,0,1 +BRDA:281,25,1,0 +BRDA:286,26,0,0 +BRDA:286,26,1,1 +BRDA:292,27,0,0 +BRDA:292,27,1,1 +BRDA:315,28,0,0 +BRDA:315,29,0,0 +BRDA:327,30,0,0 +BRDA:327,30,1,0 +BRDA:353,31,0,0 +BRDA:353,31,1,0 +BRF:60 +BRH:28 +end_of_record +TN: +SF:src/routes/employers.js +FN:9,(anonymous_0) +FN:25,(anonymous_1) +FN:56,(anonymous_2) +FN:108,(anonymous_3) +FN:201,(anonymous_4) +FNF:5 +FNH:3 +FNDA:1,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:11,(anonymous_2) +FNDA:1,(anonymous_3) +FNDA:0,(anonymous_4) +DA:1,7 +DA:2,7 +DA:3,7 +DA:4,7 +DA:6,7 +DA:9,7 +DA:10,1 +DA:11,1 +DA:17,1 +DA:19,0 +DA:20,0 +DA:25,7 +DA:26,0 +DA:27,0 +DA:29,0 +DA:36,0 +DA:37,0 +DA:40,0 +DA:42,0 +DA:43,0 +DA:48,7 +DA:57,11 +DA:58,11 +DA:59,11 +DA:60,0 +DA:71,11 +DA:74,11 +DA:79,11 +DA:80,0 +DA:83,11 +DA:89,11 +DA:94,0 +DA:95,0 +DA:100,7 +DA:109,1 +DA:110,1 +DA:111,1 +DA:112,0 +DA:115,1 +DA:124,1 +DA:127,1 +DA:132,1 +DA:133,0 +DA:137,1 +DA:138,0 +DA:141,1 +DA:142,1 +DA:143,1 +DA:145,1 +DA:146,0 +DA:147,0 +DA:148,0 +DA:150,1 +DA:151,0 +DA:152,0 +DA:153,0 +DA:155,1 +DA:156,0 +DA:157,0 +DA:158,0 +DA:160,1 +DA:161,0 +DA:162,0 +DA:163,0 +DA:165,1 +DA:166,1 +DA:167,1 +DA:168,1 +DA:170,1 +DA:171,0 +DA:172,0 +DA:173,0 +DA:175,1 +DA:176,0 +DA:177,0 +DA:178,0 +DA:181,1 +DA:182,0 +DA:185,1 +DA:186,1 +DA:188,1 +DA:190,1 +DA:195,0 +DA:196,0 +DA:201,7 +DA:202,0 +DA:203,0 +DA:204,0 +DA:206,0 +DA:210,0 +DA:211,0 +DA:213,0 +DA:214,0 +DA:215,0 +DA:216,0 +DA:219,0 +DA:222,0 +DA:223,0 +DA:224,0 +DA:225,0 +DA:226,0 +DA:227,0 +DA:228,0 +DA:230,0 +DA:233,0 +DA:234,0 +DA:235,0 +DA:236,0 +DA:237,0 +DA:239,0 +DA:241,0 +DA:251,0 +DA:252,0 +DA:256,7 +LF:114 +LH:48 +BRDA:36,0,0,0 +BRDA:36,0,1,0 +BRDA:59,1,0,0 +BRDA:59,1,1,11 +BRDA:79,2,0,0 +BRDA:79,2,1,11 +BRDA:111,3,0,0 +BRDA:111,3,1,1 +BRDA:132,4,0,0 +BRDA:132,4,1,1 +BRDA:137,5,0,0 +BRDA:137,5,1,1 +BRDA:137,6,0,1 +BRDA:137,6,1,0 +BRDA:145,7,0,0 +BRDA:145,7,1,1 +BRDA:150,8,0,0 +BRDA:150,8,1,1 +BRDA:155,9,0,0 +BRDA:155,9,1,1 +BRDA:160,10,0,0 +BRDA:160,10,1,1 +BRDA:165,11,0,1 +BRDA:165,11,1,0 +BRDA:170,12,0,0 +BRDA:170,12,1,1 +BRDA:175,13,0,0 +BRDA:175,13,1,1 +BRDA:181,14,0,0 +BRDA:181,14,1,1 +BRDA:204,15,0,0 +BRDA:204,16,0,0 +BRDA:213,17,0,0 +BRDA:213,17,1,0 +BRDA:235,18,0,0 +BRDA:235,18,1,0 +BRF:36 +BRH:14 +end_of_record +TN: +SF:src/routes/jobs.js +FN:9,(anonymous_0) +FN:64,(anonymous_1) +FN:142,(anonymous_2) +FN:186,(anonymous_3) +FN:224,(anonymous_4) +FN:301,(anonymous_5) +FN:446,(anonymous_6) +FNF:7 +FNH:5 +FNDA:3,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:2,(anonymous_3) +FNDA:10,(anonymous_4) +FNDA:1,(anonymous_5) +FNDA:1,(anonymous_6) +DA:1,7 +DA:2,7 +DA:3,7 +DA:4,7 +DA:6,7 +DA:9,7 +DA:10,3 +DA:23,3 +DA:25,3 +DA:30,3 +DA:31,3 +DA:32,3 +DA:35,3 +DA:36,3 +DA:37,3 +DA:39,3 +DA:40,1 +DA:41,1 +DA:42,1 +DA:45,3 +DA:46,1 +DA:47,1 +DA:48,1 +DA:51,3 +DA:52,0 +DA:53,0 +DA:54,0 +DA:57,3 +DA:58,0 +DA:59,0 +DA:60,0 +DA:63,3 +DA:64,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:70,3 +DA:71,0 +DA:72,0 +DA:73,0 +DA:76,3 +DA:77,0 +DA:78,0 +DA:79,0 +DA:82,3 +DA:83,0 +DA:86,3 +DA:87,3 +DA:90,3 +DA:93,3 +DA:94,3 +DA:95,3 +DA:96,3 +DA:97,3 +DA:98,3 +DA:99,3 +DA:101,3 +DA:104,3 +DA:109,3 +DA:110,3 +DA:111,3 +DA:113,3 +DA:114,3 +DA:115,3 +DA:117,3 +DA:118,1 +DA:119,1 +DA:120,1 +DA:123,3 +DA:124,1 +DA:125,1 +DA:126,1 +DA:129,3 +DA:130,0 +DA:131,0 +DA:132,0 +DA:135,3 +DA:136,0 +DA:137,0 +DA:138,0 +DA:141,3 +DA:142,0 +DA:143,0 +DA:144,0 +DA:145,0 +DA:148,3 +DA:149,0 +DA:150,0 +DA:151,0 +DA:154,3 +DA:155,0 +DA:156,0 +DA:157,0 +DA:160,3 +DA:161,0 +DA:164,3 +DA:165,3 +DA:168,3 +DA:170,3 +DA:180,0 +DA:181,0 +DA:186,7 +DA:187,2 +DA:188,2 +DA:190,2 +DA:197,2 +DA:198,1 +DA:201,1 +DA:203,0 +DA:204,0 +DA:209,7 +DA:225,10 +DA:226,10 +DA:227,10 +DA:228,1 +DA:246,9 +DA:250,9 +DA:251,9 +DA:255,9 +DA:256,0 +DA:258,9 +DA:261,0 +DA:262,0 +DA:263,0 +DA:265,0 +DA:268,9 +DA:274,9 +DA:279,0 +DA:280,0 +DA:285,7 +DA:302,1 +DA:303,1 +DA:304,1 +DA:305,0 +DA:308,1 +DA:325,1 +DA:328,1 +DA:335,1 +DA:336,0 +DA:339,1 +DA:342,1 +DA:343,0 +DA:346,1 +DA:347,1 +DA:348,1 +DA:350,1 +DA:351,1 +DA:352,1 +DA:353,1 +DA:355,1 +DA:356,1 +DA:357,1 +DA:358,1 +DA:360,1 +DA:361,0 +DA:362,0 +DA:363,0 +DA:365,1 +DA:366,0 +DA:367,0 +DA:368,0 +DA:370,1 +DA:371,0 +DA:372,0 +DA:373,0 +DA:375,1 +DA:376,0 +DA:377,0 +DA:378,0 +DA:380,1 +DA:381,0 +DA:382,0 +DA:383,0 +DA:385,1 +DA:386,0 +DA:387,0 +DA:388,0 +DA:390,1 +DA:391,0 +DA:392,0 +DA:393,0 +DA:395,1 +DA:396,0 +DA:397,0 +DA:398,0 +DA:400,1 +DA:401,0 +DA:402,0 +DA:403,0 +DA:405,1 +DA:406,0 +DA:407,0 +DA:408,0 +DA:410,1 +DA:411,0 +DA:412,0 +DA:413,0 +DA:415,1 +DA:416,0 +DA:417,0 +DA:418,0 +DA:420,1 +DA:421,0 +DA:422,0 +DA:423,0 +DA:426,1 +DA:427,0 +DA:430,1 +DA:431,1 +DA:433,1 +DA:435,1 +DA:440,0 +DA:441,0 +DA:446,7 +DA:447,1 +DA:448,1 +DA:451,1 +DA:458,1 +DA:459,0 +DA:462,1 +DA:465,1 +DA:466,0 +DA:469,1 +DA:471,1 +DA:473,0 +DA:474,0 +DA:478,7 +LF:227 +LH:133 +BRDA:20,0,0,3 +BRDA:21,1,0,3 +BRDA:22,2,0,3 +BRDA:39,3,0,1 +BRDA:39,3,1,2 +BRDA:45,4,0,1 +BRDA:45,4,1,2 +BRDA:51,5,0,0 +BRDA:51,5,1,3 +BRDA:57,6,0,0 +BRDA:57,6,1,3 +BRDA:63,7,0,0 +BRDA:63,7,1,3 +BRDA:70,8,0,0 +BRDA:70,8,1,3 +BRDA:76,9,0,0 +BRDA:76,9,1,3 +BRDA:82,10,0,0 +BRDA:82,10,1,3 +BRDA:86,11,0,3 +BRDA:86,11,1,0 +BRDA:117,12,0,1 +BRDA:117,12,1,2 +BRDA:123,13,0,1 +BRDA:123,13,1,2 +BRDA:129,14,0,0 +BRDA:129,14,1,3 +BRDA:135,15,0,0 +BRDA:135,15,1,3 +BRDA:141,16,0,0 +BRDA:141,16,1,3 +BRDA:148,17,0,0 +BRDA:148,17,1,3 +BRDA:154,18,0,0 +BRDA:154,18,1,3 +BRDA:160,19,0,0 +BRDA:160,19,1,3 +BRDA:164,20,0,3 +BRDA:164,20,1,0 +BRDA:197,21,0,1 +BRDA:197,21,1,1 +BRDA:227,22,0,1 +BRDA:227,22,1,9 +BRDA:240,23,0,9 +BRDA:241,24,0,7 +BRDA:250,25,0,9 +BRDA:250,25,1,0 +BRDA:255,26,0,0 +BRDA:255,26,1,9 +BRDA:262,27,0,0 +BRDA:262,27,1,0 +BRDA:304,28,0,0 +BRDA:304,28,1,1 +BRDA:335,29,0,0 +BRDA:335,29,1,1 +BRDA:342,30,0,0 +BRDA:342,30,1,1 +BRDA:342,31,0,1 +BRDA:342,31,1,0 +BRDA:350,32,0,1 +BRDA:350,32,1,0 +BRDA:355,33,0,1 +BRDA:355,33,1,0 +BRDA:360,34,0,0 +BRDA:360,34,1,1 +BRDA:365,35,0,0 +BRDA:365,35,1,1 +BRDA:370,36,0,0 +BRDA:370,36,1,1 +BRDA:375,37,0,0 +BRDA:375,37,1,1 +BRDA:380,38,0,0 +BRDA:380,38,1,1 +BRDA:385,39,0,0 +BRDA:385,39,1,1 +BRDA:390,40,0,0 +BRDA:390,40,1,1 +BRDA:395,41,0,0 +BRDA:395,41,1,1 +BRDA:400,42,0,0 +BRDA:400,42,1,1 +BRDA:405,43,0,0 +BRDA:405,43,1,1 +BRDA:410,44,0,0 +BRDA:410,44,1,1 +BRDA:415,45,0,0 +BRDA:415,45,1,1 +BRDA:420,46,0,0 +BRDA:420,46,1,1 +BRDA:426,47,0,0 +BRDA:426,47,1,1 +BRDA:458,48,0,0 +BRDA:458,48,1,1 +BRDA:465,49,0,0 +BRDA:465,49,1,1 +BRDA:465,50,0,1 +BRDA:465,50,1,0 +BRF:97 +BRH:56 +end_of_record +TN: +SF:src/routes/resumes.js +FN:15,(anonymous_0) +FN:22,(anonymous_1) +FN:34,(anonymous_2) +FN:51,(anonymous_3) +FN:81,(anonymous_4) +FN:117,(anonymous_5) +FN:168,(anonymous_6) +FN:209,(anonymous_7) +FN:258,(anonymous_8) +FNF:9 +FNH:8 +FNDA:1,(anonymous_0) +FNDA:1,(anonymous_1) +FNDA:1,(anonymous_2) +FNDA:1,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:1,(anonymous_5) +FNDA:1,(anonymous_6) +FNDA:1,(anonymous_7) +FNDA:1,(anonymous_8) +DA:1,7 +DA:2,7 +DA:3,7 +DA:4,7 +DA:5,7 +DA:6,7 +DA:7,7 +DA:8,7 +DA:9,7 +DA:11,7 +DA:14,7 +DA:16,1 +DA:17,1 +DA:18,0 +DA:20,1 +DA:23,1 +DA:24,1 +DA:25,1 +DA:29,7 +DA:35,1 +DA:42,1 +DA:43,1 +DA:45,0 +DA:51,7 +DA:52,1 +DA:53,1 +DA:56,1 +DA:57,1 +DA:61,1 +DA:62,0 +DA:64,0 +DA:65,0 +DA:68,1 +DA:73,1 +DA:75,0 +DA:76,0 +DA:81,7 +DA:82,0 +DA:83,0 +DA:85,0 +DA:90,0 +DA:91,0 +DA:94,0 +DA:97,0 +DA:98,0 +DA:102,0 +DA:103,0 +DA:105,0 +DA:106,0 +DA:109,0 +DA:111,0 +DA:112,0 +DA:117,7 +DA:118,1 +DA:119,1 +DA:120,0 +DA:124,1 +DA:129,1 +DA:130,0 +DA:133,1 +DA:136,1 +DA:137,1 +DA:143,1 +DA:157,1 +DA:162,0 +DA:163,0 +DA:168,7 +DA:169,1 +DA:170,1 +DA:172,1 +DA:177,1 +DA:178,0 +DA:181,1 +DA:184,1 +DA:185,1 +DA:189,1 +DA:190,0 +DA:192,0 +DA:193,0 +DA:197,1 +DA:198,0 +DA:201,1 +DA:203,0 +DA:204,0 +DA:209,7 +DA:210,1 +DA:211,1 +DA:214,1 +DA:219,1 +DA:220,0 +DA:223,1 +DA:226,1 +DA:231,1 +DA:232,0 +DA:236,1 +DA:242,1 +DA:247,1 +DA:252,0 +DA:253,0 +DA:258,7 +DA:259,1 +DA:260,1 +DA:263,1 +DA:268,1 +DA:269,0 +DA:272,1 +DA:275,1 +DA:280,1 +DA:281,0 +DA:284,1 +DA:287,1 +DA:288,1 +DA:292,1 +DA:294,1 +DA:296,0 +DA:297,0 +DA:301,7 +LF:117 +LH:76 +BRDA:17,0,0,0 +BRDA:17,0,1,1 +BRDA:24,1,0,1 +BRDA:24,1,1,0 +BRDA:42,2,0,1 +BRDA:42,2,1,0 +BRDA:56,3,0,1 +BRDA:56,3,1,0 +BRDA:61,4,0,0 +BRDA:61,4,1,1 +BRDA:61,5,0,1 +BRDA:61,5,1,1 +BRDA:64,6,0,0 +BRDA:64,6,1,0 +BRDA:64,7,0,0 +BRDA:64,7,1,0 +BRDA:64,7,2,0 +BRDA:90,8,0,0 +BRDA:90,8,1,0 +BRDA:97,9,0,0 +BRDA:97,9,1,0 +BRDA:102,10,0,0 +BRDA:102,10,1,0 +BRDA:102,11,0,0 +BRDA:102,11,1,0 +BRDA:105,12,0,0 +BRDA:105,12,1,0 +BRDA:105,13,0,0 +BRDA:105,13,1,0 +BRDA:105,13,2,0 +BRDA:119,14,0,0 +BRDA:119,14,1,1 +BRDA:129,15,0,0 +BRDA:129,15,1,1 +BRDA:136,16,0,1 +BRDA:136,16,1,0 +BRDA:177,17,0,0 +BRDA:177,17,1,1 +BRDA:184,18,0,1 +BRDA:184,18,1,0 +BRDA:189,19,0,0 +BRDA:189,19,1,1 +BRDA:189,20,0,1 +BRDA:189,20,1,1 +BRDA:192,21,0,0 +BRDA:192,21,1,0 +BRDA:192,22,0,0 +BRDA:192,22,1,0 +BRDA:192,22,2,0 +BRDA:197,23,0,0 +BRDA:197,23,1,1 +BRDA:219,24,0,0 +BRDA:219,24,1,1 +BRDA:231,25,0,0 +BRDA:231,25,1,1 +BRDA:268,26,0,0 +BRDA:268,26,1,1 +BRDA:280,27,0,0 +BRDA:280,27,1,1 +BRDA:287,28,0,1 +BRDA:287,28,1,0 +BRF:61 +BRH:21 +end_of_record +TN: +SF:src/routes/users.js +FN:9,(anonymous_0) +FN:22,(anonymous_1) +FN:52,(anonymous_2) +FN:118,(anonymous_3) +FN:142,(anonymous_4) +FNF:5 +FNH:4 +FNDA:1,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:1,(anonymous_2) +FNDA:1,(anonymous_3) +FNDA:1,(anonymous_4) +DA:1,7 +DA:2,7 +DA:3,7 +DA:4,7 +DA:6,7 +DA:9,7 +DA:10,1 +DA:11,1 +DA:14,1 +DA:16,0 +DA:17,0 +DA:22,7 +DA:23,0 +DA:24,0 +DA:27,0 +DA:28,0 +DA:31,0 +DA:36,0 +DA:37,0 +DA:40,0 +DA:42,0 +DA:43,0 +DA:48,7 +DA:53,1 +DA:54,1 +DA:55,1 +DA:56,0 +DA:59,1 +DA:60,1 +DA:63,1 +DA:64,0 +DA:68,1 +DA:69,0 +DA:73,0 +DA:74,0 +DA:78,1 +DA:79,1 +DA:80,1 +DA:82,1 +DA:83,1 +DA:84,1 +DA:85,1 +DA:87,1 +DA:88,0 +DA:89,0 +DA:90,0 +DA:92,1 +DA:93,0 +DA:94,0 +DA:95,0 +DA:98,1 +DA:99,0 +DA:102,1 +DA:103,1 +DA:105,1 +DA:107,1 +DA:112,0 +DA:113,0 +DA:118,7 +DA:119,1 +DA:120,1 +DA:122,1 +DA:127,1 +DA:128,0 +DA:131,1 +DA:136,0 +DA:137,0 +DA:142,7 +DA:143,1 +DA:144,1 +DA:146,1 +DA:151,1 +DA:152,0 +DA:155,1 +DA:160,0 +DA:161,0 +DA:165,7 +LF:77 +LH:45 +BRDA:27,0,0,0 +BRDA:27,0,1,0 +BRDA:27,1,0,0 +BRDA:27,1,1,0 +BRDA:36,2,0,0 +BRDA:36,2,1,0 +BRDA:55,3,0,0 +BRDA:55,3,1,1 +BRDA:63,4,0,0 +BRDA:63,4,1,1 +BRDA:63,5,0,1 +BRDA:63,5,1,0 +BRDA:68,6,0,0 +BRDA:68,6,1,1 +BRDA:73,7,0,0 +BRDA:73,7,1,0 +BRDA:82,8,0,1 +BRDA:82,8,1,0 +BRDA:87,9,0,0 +BRDA:87,9,1,1 +BRDA:92,10,0,0 +BRDA:92,10,1,1 +BRDA:98,11,0,0 +BRDA:98,11,1,1 +BRDA:127,12,0,0 +BRDA:127,12,1,1 +BRDA:151,13,0,0 +BRDA:151,13,1,1 +BRF:28 +BRH:10 +end_of_record +TN: +SF:src/tests/utils.js +FN:10,(anonymous_0) +FN:14,registerUser +FN:38,createEmployerProfile +FN:58,createAdminUser +FN:80,ensureUploadDir +FN:88,cleanupUploads +FNF:6 +FNH:6 +FNDA:16,(anonymous_0) +FNDA:15,registerUser +FNDA:11,createEmployerProfile +FNDA:1,createAdminUser +FNDA:24,ensureUploadDir +FNDA:23,cleanupUploads +DA:1,7 +DA:2,7 +DA:3,7 +DA:4,7 +DA:5,7 +DA:6,7 +DA:7,7 +DA:8,7 +DA:10,16 +DA:12,7 +DA:15,15 +DA:24,15 +DA:28,15 +DA:39,11 +DA:50,11 +DA:55,11 +DA:59,1 +DA:60,1 +DA:61,1 +DA:63,1 +DA:68,1 +DA:72,1 +DA:81,24 +DA:82,24 +DA:83,0 +DA:85,24 +DA:89,23 +DA:90,23 +DA:91,0 +DA:93,23 +DA:94,23 +DA:95,0 +DA:96,0 +DA:97,0 +DA:104,7 +LF:35 +LH:30 +BRDA:10,0,0,0 +BRDA:14,1,0,0 +BRDA:14,2,0,15 +BRDA:16,3,0,15 +BRDA:16,3,1,15 +BRDA:17,4,0,15 +BRDA:17,4,1,15 +BRDA:18,5,0,15 +BRDA:18,5,1,15 +BRDA:19,6,0,15 +BRDA:19,6,1,15 +BRDA:38,7,0,10 +BRDA:40,8,0,11 +BRDA:40,8,1,10 +BRDA:41,9,0,11 +BRDA:41,9,1,10 +BRDA:42,10,0,11 +BRDA:42,10,1,11 +BRDA:43,11,0,11 +BRDA:43,11,1,11 +BRDA:44,12,0,11 +BRDA:44,12,1,11 +BRDA:45,13,0,11 +BRDA:45,13,1,11 +BRDA:46,14,0,11 +BRDA:46,14,1,11 +BRDA:82,15,0,0 +BRDA:82,15,1,24 +BRDA:90,16,0,0 +BRDA:90,16,1,23 +BRF:30 +BRH:26 +end_of_record diff --git a/backend/jest.config.js b/backend/jest.config.js new file mode 100644 index 0000000..d123dc9 --- /dev/null +++ b/backend/jest.config.js @@ -0,0 +1,15 @@ +module.exports = { + testEnvironment: 'node', + setupFilesAfterEnv: ['/src/tests/setup.js'], + globalSetup: '/src/tests/globalSetup.js', + globalTeardown: '/src/tests/globalTeardown.js', + coveragePathIgnorePatterns: ['/node_modules/'], + coverageThreshold: { + global: { + statements: 60, + branches: 40, + functions: 55, + lines: 60 + } + } +}; diff --git a/backend/package-lock.json b/backend/package-lock.json index f94167b..d192f56 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -13,16 +13,20 @@ "cors": "^2.8.5", "dotenv": "^16.3.1", "express": "^4.18.2", + "express-rate-limit": "^6.10.0", "express-validator": "^7.0.1", "helmet": "^7.1.0", "jsonwebtoken": "^9.0.2", "morgan": "^1.10.0", - "multer": "^1.4.5-lts.1", + "multer": "^2.0.0", "pg": "^8.11.3", + "sanitize-filename": "^1.6.3", "uuid": "^9.0.1" }, "devDependencies": { "@types/jest": "^29.5.8", + "eslint": "^8.57.0", + "eslint-plugin-jest": "^27.9.0", "jest": "^29.7.0", "nodemon": "^3.0.2", "supertest": "^6.3.3" @@ -574,6 +578,177 @@ "dev": true, "license": "MIT" }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -956,6 +1131,44 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@paralleldrive/cuid2": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", @@ -1086,6 +1299,13 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.8.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.8.0.tgz", @@ -1096,6 +1316,13 @@ "undici-types": "~7.14.0" } }, + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -1120,6 +1347,193 @@ "dev": true, "license": "MIT" }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -1133,6 +1547,46 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1211,6 +1665,16 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -1748,17 +2212,17 @@ "license": "MIT" }, "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", "engines": [ - "node >= 0.8" + "node >= 6.0" ], "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", - "readable-stream": "^2.2.2", + "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, @@ -1812,12 +2276,6 @@ "dev": true, "license": "MIT" }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -1892,6 +2350,13 @@ } } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -1962,6 +2427,32 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dotenv": { "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", @@ -2121,6 +2612,257 @@ "node": ">=8" } }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jest": { + "version": "27.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz", + "integrity": "sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^5.10.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0 || ^7.0.0", + "eslint": "^7.0.0 || ^8.0.0", + "jest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -2135,6 +2877,52 @@ "node": ">=4" } }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -2240,6 +3028,18 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-rate-limit": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.11.2.tgz", + "integrity": "sha512-a7uwwfNTh1U60ssiIkuLFWHt4hAC5yxlLGU2VP0X4YNlyEDZAqF4tK3GD3NSitVBrCQmQ0++0uOyFOgC2y4DDw==", + "license": "MIT", + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "express": "^4 || ^5" + } + }, "node_modules/express-validator": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.2.1.tgz", @@ -2253,6 +3053,30 @@ "node": ">= 8.0.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2260,6 +3084,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", @@ -2267,6 +3098,16 @@ "dev": true, "license": "MIT" }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -2277,6 +3118,19 @@ "bser": "2.1.1" } }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -2322,6 +3176,28 @@ "node": ">=8" } }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, "node_modules/form-data": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", @@ -2519,6 +3395,56 @@ "node": ">= 6" } }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -2538,6 +3464,13 @@ "dev": true, "license": "ISC" }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2642,6 +3575,16 @@ "node": ">=0.10.0" } }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -2649,6 +3592,33 @@ "dev": true, "license": "ISC" }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", @@ -2795,6 +3765,16 @@ "node": ">=0.12.0" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -2808,12 +3788,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3560,6 +4534,13 @@ "node": ">=6" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -3567,6 +4548,20 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -3641,6 +4636,16 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -3661,6 +4666,20 @@ "node": ">=6" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -3723,6 +4742,13 @@ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "license": "MIT" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -3812,6 +4838,16 @@ "dev": true, "license": "MIT" }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -3947,22 +4983,21 @@ "license": "MIT" }, "node_modules/multer": { - "version": "1.4.5-lts.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", - "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", - "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", "license": "MIT", "dependencies": { "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" + "type-is": "^1.6.18", + "xtend": "^4.0.2" }, "engines": { - "node": ">= 6.0.0" + "node": ">= 10.16.0" } }, "node_modules/natural-compare": { @@ -4176,6 +5211,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -4231,6 +5284,19 @@ "node": ">=6" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -4302,6 +5368,16 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/pg": { "version": "8.16.3", "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", @@ -4473,6 +5549,16 @@ "node": ">=0.10.0" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -4501,12 +5587,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -4541,6 +5621,16 @@ "dev": true, "license": "MIT" }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -4573,6 +5663,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -4605,26 +5716,19 @@ "license": "MIT" }, "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -4702,6 +5806,58 @@ "node": ">=10" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -4728,6 +5884,15 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -5011,20 +6176,14 @@ } }, "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", "dependencies": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -5230,6 +6389,13 @@ "node": ">=8" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -5269,6 +6435,51 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "license": "WTFPL", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -5311,6 +6522,21 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "license": "MIT" }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -5365,6 +6591,22 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "license": "(WTFPL OR MIT)" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -5452,6 +6694,16 @@ "node": ">= 8" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/backend/package.json b/backend/package.json index 6b97b4f..62d3803 100644 --- a/backend/package.json +++ b/backend/package.json @@ -9,7 +9,8 @@ "test": "jest", "test:watch": "jest --watch", "migrate": "node src/database/migrate.js", - "seed": "node src/database/seed.js" + "seed": "node src/database/seed.js", + "lint": "eslint --ext js src" }, "dependencies": { "express": "^4.18.2", @@ -18,7 +19,9 @@ "bcryptjs": "^2.4.3", "jsonwebtoken": "^9.0.2", "pg": "^8.11.3", - "multer": "^1.4.5-lts.1", + "multer": "^2.0.0", + "express-rate-limit": "^6.10.0", + "sanitize-filename": "^1.6.3", "express-validator": "^7.0.1", "dotenv": "^16.3.1", "uuid": "^9.0.1", @@ -28,7 +31,9 @@ "nodemon": "^3.0.2", "jest": "^29.7.0", "supertest": "^6.3.3", - "@types/jest": "^29.5.8" + "@types/jest": "^29.5.8", + "eslint": "^8.57.0", + "eslint-plugin-jest": "^27.9.0" }, "keywords": ["recruiter", "saas", "workflow", "hiring"], "author": "MerchantsOfHope-SupplyANdDemandPortal", diff --git a/backend/src/config/index.js b/backend/src/config/index.js index 65a23ba..7113b27 100644 --- a/backend/src/config/index.js +++ b/backend/src/config/index.js @@ -7,14 +7,39 @@ const optionalNumber = (value, fallback) => { return Number.isFinite(parsed) ? parsed : fallback; }; +const deriveDatabaseUrl = () => { + if (process.env.DATABASE_URL) { + return process.env.DATABASE_URL; + } + + const { + POSTGRES_USER = 'merchantsofhope_user', + POSTGRES_PASSWORD, + POSTGRES_DB = 'merchantsofhope_supplyanddemandportal', + POSTGRES_HOST = 'merchantsofhope-supplyanddemandportal-database', + POSTGRES_PORT = '5432' + } = process.env; + + if (!POSTGRES_PASSWORD) { + return undefined; + } + + return `postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}`; +}; + const config = { env: process.env.NODE_ENV || 'development', host: process.env.HOST || process.env.BACKEND_HOST || '0.0.0.0', port: optionalNumber(process.env.PORT || process.env.BACKEND_PORT, 3001), - databaseUrl: process.env.DATABASE_URL, + databaseUrl: deriveDatabaseUrl(), jwtSecret: process.env.JWT_SECRET, corsOrigin: process.env.CORS_ORIGIN || '*', - logLevel: process.env.LOG_LEVEL || 'info' + logLevel: process.env.LOG_LEVEL || 'info', + uploadDir: process.env.UPLOAD_DIR || 'uploads/resumes', + rateLimit: { + windowMs: optionalNumber(process.env.RATE_LIMIT_WINDOW_MS, 15 * 60 * 1000), + max: optionalNumber(process.env.RATE_LIMIT_MAX, 100) + } }; const required = ['databaseUrl', 'jwtSecret']; diff --git a/backend/src/database/connection.js b/backend/src/database/connection.js index 3e39fa9..e411b64 100644 --- a/backend/src/database/connection.js +++ b/backend/src/database/connection.js @@ -9,11 +9,14 @@ const pool = new Pool({ // Test database connection pool.on('connect', () => { if (config.logLevel !== 'silent') { - console.log('Connected to MerchantsOfHope-SupplyANdDemandPortal database'); + console.info('Connected to MerchantsOfHope-SupplyANdDemandPortal database'); } }); pool.on('error', (err) => { + if (err.code === '57P01' || err.message?.includes('terminating connection')) { + return; + } console.error('Database connection error:', err); }); diff --git a/backend/src/database/migrate.js b/backend/src/database/migrate.js index 3b61f3b..e7dafd8 100644 --- a/backend/src/database/migrate.js +++ b/backend/src/database/migrate.js @@ -4,7 +4,7 @@ const pool = require('./connection'); async function migrate() { try { - console.log('Starting database migration...'); + console.info('Starting database migration...'); // Read schema file const schemaPath = path.join(__dirname, 'schema.sql'); @@ -13,7 +13,7 @@ async function migrate() { // Execute schema await pool.query(schema); - console.log('Database migration completed successfully!'); + console.info('Database migration completed successfully!'); } catch (error) { console.error('Migration failed:', error); process.exit(1); diff --git a/backend/src/database/seed.js b/backend/src/database/seed.js index 230c0dd..ffc14c0 100644 --- a/backend/src/database/seed.js +++ b/backend/src/database/seed.js @@ -3,7 +3,7 @@ const pool = require('./connection'); async function seed() { try { - console.log('Starting database seeding...'); + console.info('Starting database seeding...'); // Hash passwords const adminPassword = await bcrypt.hash('admin123', 10); @@ -86,7 +86,7 @@ async function seed() { ); // Insert job posting - const jobResult = await pool.query( + await pool.query( `INSERT INTO jobs (employer_id, title, description, requirements, responsibilities, location, employment_type, salary_min, salary_max, remote_allowed, experience_level, skills_required, benefits) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) RETURNING id`, [ @@ -106,12 +106,12 @@ async function seed() { ] ); - console.log('Database seeding completed successfully!'); - console.log('Sample users created:'); - console.log('- Admin: admin@merchantsofhope.org / admin123'); - console.log('- Recruiter: recruiter@merchantsofhope.org / recruiter123'); - console.log('- Employer: employer@techcorp.com / employer123'); - console.log('- Candidate: candidate@example.com / candidate123'); + console.info('Database seeding completed successfully!'); + console.info('Sample users created:'); + console.info('- Admin: admin@merchantsofhope.org / admin123'); + console.info('- Recruiter: recruiter@merchantsofhope.org / recruiter123'); + console.info('- Employer: employer@techcorp.com / employer123'); + console.info('- Candidate: candidate@example.com / candidate123'); } catch (error) { console.error('Seeding failed:', error); diff --git a/backend/src/index.js b/backend/src/index.js index fef9698..0f834b6 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -2,5 +2,5 @@ const app = require('./server'); const config = require('./config'); app.listen(config.port, config.host, () => { - console.log(`MerchantsOfHope-SupplyANdDemandPortal backend server running on ${config.host}:${config.port}`); + console.info(`MerchantsOfHope-SupplyANdDemandPortal backend server running on ${config.host}:${config.port}`); }); diff --git a/backend/src/routes/resumes.js b/backend/src/routes/resumes.js index 3e921c0..14d1e2d 100644 --- a/backend/src/routes/resumes.js +++ b/backend/src/routes/resumes.js @@ -3,22 +3,25 @@ const multer = require('multer'); const path = require('path'); const fs = require('fs'); const { v4: uuidv4 } = require('uuid'); +const sanitize = require('sanitize-filename'); const pool = require('../database/connection'); const { authenticateToken, requireRole } = require('../middleware/auth'); +const config = require('../config'); const router = express.Router(); // Configure multer for file uploads const storage = multer.diskStorage({ destination: (req, file, cb) => { - const uploadDir = path.join(__dirname, '../../uploads/resumes'); + const uploadDir = path.join(__dirname, '..', '..', config.uploadDir); if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir, { recursive: true }); } cb(null, uploadDir); }, filename: (req, file, cb) => { - const uniqueName = `${uuidv4()}-${file.originalname}`; + const safeOriginal = sanitize(file.originalname); + const uniqueName = `${uuidv4()}-${safeOriginal || 'resume'}`; cb(null, uniqueName); } }); diff --git a/backend/src/server.js b/backend/src/server.js index 325a472..b466b39 100644 --- a/backend/src/server.js +++ b/backend/src/server.js @@ -2,6 +2,7 @@ const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); const config = require('./config'); const authRoutes = require('./routes/auth'); @@ -14,6 +15,13 @@ const resumeRoutes = require('./routes/resumes'); const app = express(); +const authLimiter = rateLimit({ + windowMs: config.rateLimit.windowMs, + max: config.rateLimit.max, + standardHeaders: true, + legacyHeaders: false +}); + app.disable('x-powered-by'); const corsOrigins = config.corsOrigin === '*' @@ -26,7 +34,7 @@ app.use(morgan(config.env === 'production' ? 'combined' : 'dev')); app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true })); -app.use('/api/auth', authRoutes); +app.use('/api/auth', authLimiter, authRoutes); app.use('/api/users', userRoutes); app.use('/api/employers', employerRoutes); app.use('/api/candidates', candidateRoutes); diff --git a/backend/src/tests/applications.test.js b/backend/src/tests/applications.test.js new file mode 100644 index 0000000..351397e --- /dev/null +++ b/backend/src/tests/applications.test.js @@ -0,0 +1,79 @@ +const request = require('supertest'); +const app = require('../server'); +const { registerUser, createEmployerProfile } = require('./utils'); + +async function createJob(token) { + const response = await request(app) + .post('/api/jobs') + .set('Authorization', `Bearer ${token}`) + .send({ + title: 'QA Engineer', + description: 'Ensure software quality', + requirements: ['Attention to detail'], + responsibilities: ['Write test plans'], + location: 'Remote', + employmentType: 'full-time', + remoteAllowed: true + }) + .expect(201); + + return response.body.job; +} + +describe('Applications API', () => { + it('supports full application lifecycle', async () => { + const { token: employerToken } = await registerUser('employer'); + await createEmployerProfile(employerToken); + const job = await createJob(employerToken); + + const { token: candidateToken } = await registerUser('candidate'); + + await request(app) + .post('/api/candidates') + .set('Authorization', `Bearer ${candidateToken}`) + .send({ + location: 'Remote', + skills: ['Automation'], + experienceLevel: 'mid' + }) + .expect(201); + + const applicationResponse = await request(app) + .post('/api/applications') + .set('Authorization', `Bearer ${candidateToken}`) + .send({ + jobId: job.id, + coverLetter: 'Excited to apply' + }) + .expect(201); + + const applicationId = applicationResponse.body.application.id; + + const listForCandidate = await request(app) + .get('/api/applications') + .set('Authorization', `Bearer ${candidateToken}`) + .expect(200); + + expect(listForCandidate.body.applications).toHaveLength(1); + + await request(app) + .put(`/api/applications/${applicationId}/status`) + .set('Authorization', `Bearer ${employerToken}`) + .send({ status: 'reviewed' }) + .expect(200); + + await request(app) + .put(`/api/applications/${applicationId}/notes`) + .set('Authorization', `Bearer ${employerToken}`) + .send({ notes: 'Strong automation background' }) + .expect(200); + + const detailResponse = await request(app) + .get(`/api/applications/${applicationId}`) + .set('Authorization', `Bearer ${employerToken}`) + .expect(200); + + expect(detailResponse.body.status).toBe('reviewed'); + expect(detailResponse.body.notes).toBe('Strong automation background'); + }); +}); diff --git a/backend/src/tests/auth.test.js b/backend/src/tests/auth.test.js index 7291378..9771c0e 100644 --- a/backend/src/tests/auth.test.js +++ b/backend/src/tests/auth.test.js @@ -1,23 +1,13 @@ const request = require('supertest'); const app = require('../server'); -const pool = require('../database/connection'); - describe('Authentication', () => { - beforeEach(async () => { - // Clean up database before each test - await pool.query('DELETE FROM users WHERE email LIKE $1', ['test%']); - }); - - afterAll(async () => { - await pool.end(); - }); - describe('POST /api/auth/register', () => { it('should register a new user successfully', async () => { + const email = `test-${Date.now()}@example.com`; const userData = { firstName: 'Test', lastName: 'User', - email: 'test@example.com', + email, password: 'password123', role: 'candidate' }; @@ -34,10 +24,11 @@ describe('Authentication', () => { }); it('should return error for duplicate email', async () => { + const email = `test-${Date.now()}@example.com`; const userData = { firstName: 'Test', lastName: 'User', - email: 'test@example.com', + email, password: 'password123', role: 'candidate' }; @@ -57,10 +48,11 @@ describe('Authentication', () => { }); it('should return error for invalid role', async () => { + const email = `test-${Date.now()}@example.com`; const userData = { firstName: 'Test', lastName: 'User', - email: 'test@example.com', + email, password: 'password123', role: 'invalid_role' }; @@ -75,12 +67,15 @@ describe('Authentication', () => { }); describe('POST /api/auth/login', () => { + let loginEmail; + beforeEach(async () => { // Create a test user + loginEmail = `test-${Date.now()}@example.com`; const userData = { firstName: 'Test', lastName: 'User', - email: 'test@example.com', + email: loginEmail, password: 'password123', role: 'candidate' }; @@ -92,7 +87,7 @@ describe('Authentication', () => { it('should login with valid credentials', async () => { const loginData = { - email: 'test@example.com', + email: loginEmail, password: 'password123' }; @@ -108,7 +103,7 @@ describe('Authentication', () => { it('should return error for invalid credentials', async () => { const loginData = { - email: 'test@example.com', + email: loginEmail, password: 'wrongpassword' }; @@ -137,13 +132,15 @@ describe('Authentication', () => { describe('GET /api/auth/me', () => { let token; + let email; beforeEach(async () => { // Create a test user and get token + email = `test-${Date.now()}@example.com`; const userData = { firstName: 'Test', lastName: 'User', - email: 'test@example.com', + email, password: 'password123', role: 'candidate' }; @@ -162,7 +159,7 @@ describe('Authentication', () => { .expect(200); expect(response.body).toHaveProperty('user'); - expect(response.body.user.email).toBe('test@example.com'); + expect(response.body.user.email).toBe(email); }); it('should return error without token', async () => { diff --git a/backend/src/tests/candidates.test.js b/backend/src/tests/candidates.test.js new file mode 100644 index 0000000..b424a7f --- /dev/null +++ b/backend/src/tests/candidates.test.js @@ -0,0 +1,44 @@ +const request = require('supertest'); +const app = require('../server'); +const { registerUser } = require('./utils'); + +describe('Candidates API', () => { + it('creates and retrieves candidate profiles with filters', async () => { + const { token } = await registerUser('candidate'); + + const createResponse = await request(app) + .post('/api/candidates') + .set('Authorization', `Bearer ${token}`) + .send({ + phone: '+1-555-5555', + location: 'Austin, TX', + skills: ['JavaScript', 'React'], + experienceLevel: 'mid' + }) + .expect(201); + + const candidateId = createResponse.body.candidate.id; + + const listResponse = await request(app) + .get('/api/candidates?skills=React&location=Austin') + .set('Authorization', `Bearer ${token}`) + .expect(200); + + expect(listResponse.body.candidates).toHaveLength(1); + + const detailResponse = await request(app) + .get(`/api/candidates/${candidateId}`) + .set('Authorization', `Bearer ${token}`) + .expect(200); + + expect(detailResponse.body.skills).toContain('React'); + + const updateResponse = await request(app) + .put(`/api/candidates/${candidateId}`) + .set('Authorization', `Bearer ${token}`) + .send({ availability: 'Immediate' }) + .expect(200); + + expect(updateResponse.body.candidate.availability).toBe('Immediate'); + }); +}); diff --git a/backend/src/tests/employers.test.js b/backend/src/tests/employers.test.js new file mode 100644 index 0000000..c22f359 --- /dev/null +++ b/backend/src/tests/employers.test.js @@ -0,0 +1,34 @@ +const request = require('supertest'); +const app = require('../server'); +const { registerUser, createEmployerProfile } = require('./utils'); + +describe('Employers API', () => { + it('allows an employer to create and update their profile', async () => { + const { token } = await registerUser('employer'); + + const employer = await createEmployerProfile(token, { + companyName: 'Acme Corp', + industry: 'Manufacturing' + }); + + expect(employer).toMatchObject({ + company_name: 'Acme Corp', + industry: 'Manufacturing' + }); + + const listResponse = await request(app) + .get('/api/employers') + .set('Authorization', `Bearer ${token}`) + .expect(200); + + expect(listResponse.body.length).toBeGreaterThan(0); + + const updateResponse = await request(app) + .put(`/api/employers/${employer.id}`) + .set('Authorization', `Bearer ${token}`) + .send({ description: 'Updated description' }) + .expect(200); + + expect(updateResponse.body.employer.description).toBe('Updated description'); + }); +}); diff --git a/backend/src/tests/fixtures/resume.txt b/backend/src/tests/fixtures/resume.txt new file mode 100644 index 0000000..14ad6e2 --- /dev/null +++ b/backend/src/tests/fixtures/resume.txt @@ -0,0 +1 @@ +Sample resume content for testing. diff --git a/backend/src/tests/globalSetup.js b/backend/src/tests/globalSetup.js new file mode 100644 index 0000000..b464ce6 --- /dev/null +++ b/backend/src/tests/globalSetup.js @@ -0,0 +1,65 @@ +const { spawnSync } = require('child_process'); +const path = require('path'); + +const sleep = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms); + +module.exports = async () => { + process.env.NODE_ENV = process.env.NODE_ENV || 'test'; + process.env.JWT_SECRET = process.env.JWT_SECRET || 'merchantsofhope_test_secret'; + process.env.POSTGRES_DB = process.env.POSTGRES_DB || 'merchantsofhope_test'; + process.env.POSTGRES_USER = process.env.POSTGRES_USER || 'postgres'; + process.env.POSTGRES_PASSWORD = process.env.POSTGRES_PASSWORD || 'postgres'; + process.env.POSTGRES_HOST = process.env.POSTGRES_HOST || '127.0.0.1'; + process.env.POSTGRES_PORT = process.env.POSTGRES_PORT || '55432'; + + const composeFile = path.join(__dirname, '..', '..', '..', 'docker-compose.test.yml'); + + const upResult = spawnSync( + 'docker', + ['compose', '-f', composeFile, 'up', '-d', 'merchantsofhope-supplyanddemandportal-test-database'], + { + cwd: path.join(__dirname, '..', '..'), + stdio: 'inherit', + env: process.env + } + ); + + if (upResult.status !== 0) { + throw new Error('Failed to start test database container'); + } + + let ready = false; + for (let attempt = 0; attempt < 30; attempt += 1) { + const health = spawnSync( + 'docker', + ['compose', '-f', composeFile, 'exec', '-T', 'merchantsofhope-supplyanddemandportal-test-database', 'pg_isready', '-U', process.env.POSTGRES_USER], + { + cwd: path.join(__dirname, '..', '..'), + env: process.env, + stdio: 'ignore' + } + ); + + if (health.status === 0) { + ready = true; + break; + } + + sleep(1000); + } + + if (!ready) { + throw new Error('Database did not become ready in time'); + } + + const migratePath = path.join(__dirname, '..', 'database', 'migrate.js'); + const result = spawnSync('node', [migratePath], { + cwd: path.join(__dirname, '..', '..'), + stdio: 'inherit', + env: process.env + }); + + if (result.status !== 0) { + throw new Error('Database migration failed before running tests'); + } +}; diff --git a/backend/src/tests/globalTeardown.js b/backend/src/tests/globalTeardown.js new file mode 100644 index 0000000..e22209a --- /dev/null +++ b/backend/src/tests/globalTeardown.js @@ -0,0 +1,20 @@ +const { spawnSync } = require('child_process'); +const path = require('path'); +const pool = require('../database/connection'); +const { cleanupUploads } = require('./utils'); + +module.exports = async () => { + await cleanupUploads(); + await pool.end(); + + const composeFile = path.join(__dirname, '..', '..', '..', 'docker-compose.test.yml'); + spawnSync( + 'docker', + ['compose', '-f', composeFile, 'down', '--volumes'], + { + cwd: path.join(__dirname, '..', '..'), + stdio: 'inherit', + env: process.env + } + ); +}; diff --git a/backend/src/tests/jobs.test.js b/backend/src/tests/jobs.test.js index 733a7f9..93b1d78 100644 --- a/backend/src/tests/jobs.test.js +++ b/backend/src/tests/jobs.test.js @@ -1,48 +1,14 @@ const request = require('supertest'); const app = require('../server'); -const pool = require('../database/connection'); +const { registerUser, createEmployerProfile } = require('./utils'); describe('Jobs API', () => { let authToken; - let employerId; - beforeAll(async () => { - // Create test user and get auth token - const userData = { - firstName: 'Test', - lastName: 'Employer', - email: 'employer@test.com', - password: 'password123', - role: 'employer' - }; - - const registerResponse = await request(app) - .post('/api/auth/register') - .send(userData); - - authToken = registerResponse.body.token; - - // Create employer profile - const employerData = { - companyName: 'Test Company', - industry: 'Technology', - companySize: '50-200', - website: 'https://testcompany.com', - description: 'A test company', - address: '123 Test St', - phone: '+1-555-0123' - }; - - const employerResponse = await request(app) - .post('/api/employers') - .set('Authorization', `Bearer ${authToken}`) - .send(employerData); - - employerId = employerResponse.body.employer.id; - }); - - afterAll(async () => { - await pool.end(); + beforeEach(async () => { + const { token } = await registerUser('employer'); + authToken = token; + await createEmployerProfile(token); }); describe('POST /api/jobs', () => { diff --git a/backend/src/tests/resumes.test.js b/backend/src/tests/resumes.test.js new file mode 100644 index 0000000..03e191b --- /dev/null +++ b/backend/src/tests/resumes.test.js @@ -0,0 +1,55 @@ +const path = require('path'); +const request = require('supertest'); +const app = require('../server'); +const { registerUser, ensureUploadDir } = require('./utils'); + +describe('Resumes API', () => { + it('handles resume upload lifecycle for candidates', async () => { + const { token } = await registerUser('candidate'); + + const candidateResponse = await request(app) + .post('/api/candidates') + .set('Authorization', `Bearer ${token}`) + .send({ + location: 'Houston, TX', + skills: ['Node.js'] + }) + .expect(201); + + const candidateId = candidateResponse.body.candidate.id; + const uploadDir = await ensureUploadDir(); + expect(uploadDir).toBeTruthy(); + + const uploadResponse = await request(app) + .post('/api/resumes/upload') + .set('Authorization', `Bearer ${token}`) + .field('isPrimary', 'true') + .attach('resume', path.join(__dirname, 'fixtures', 'resume.txt')) + .expect(201); + + const resumeId = uploadResponse.body.resume.id; + + const listResponse = await request(app) + .get(`/api/resumes/candidate/${candidateId}`) + .set('Authorization', `Bearer ${token}`) + .expect(200); + + expect(listResponse.body).toHaveLength(1); + expect(listResponse.body[0].is_primary).toBe(true); + + await request(app) + .put(`/api/resumes/${resumeId}/primary`) + .set('Authorization', `Bearer ${token}`) + .expect(200); + + await request(app) + .get(`/api/resumes/${resumeId}/download`) + .set('Authorization', `Bearer ${token}`) + .expect(200); + + await request(app) + .delete(`/api/resumes/${resumeId}`) + .set('Authorization', `Bearer ${token}`) + .expect(200); + }); +}); diff --git a/backend/src/tests/setup.js b/backend/src/tests/setup.js new file mode 100644 index 0000000..3f28e38 --- /dev/null +++ b/backend/src/tests/setup.js @@ -0,0 +1,17 @@ +const pool = require('../database/connection'); +const { cleanupUploads } = require('./utils'); + +afterEach(async () => { + await pool.query(` + TRUNCATE TABLE + applications, + resumes, + interviews, + jobs, + candidates, + employers, + users + RESTART IDENTITY CASCADE + `); + await cleanupUploads(); +}); diff --git a/backend/src/tests/users.test.js b/backend/src/tests/users.test.js new file mode 100644 index 0000000..a95cd8d --- /dev/null +++ b/backend/src/tests/users.test.js @@ -0,0 +1,35 @@ +const request = require('supertest'); +const app = require('../server'); +const { registerUser, createAdminUser } = require('./utils'); + +describe('Users API', () => { + it('allows admin to list users and manage activation', async () => { + const { token: adminToken } = await createAdminUser(); + const { user, token } = await registerUser('candidate'); + + const listResponse = await request(app) + .get('/api/users') + .set('Authorization', `Bearer ${adminToken}`) + .expect(200); + + expect(Array.isArray(listResponse.body)).toBe(true); + + await request(app) + .put(`/api/users/${user.id}`) + .set('Authorization', `Bearer ${token}`) + .send({ firstName: 'Updated' }) + .expect(200); + + await request(app) + .put(`/api/users/${user.id}/deactivate`) + .set('Authorization', `Bearer ${adminToken}`) + .expect(200); + + const reactivate = await request(app) + .put(`/api/users/${user.id}/activate`) + .set('Authorization', `Bearer ${adminToken}`) + .expect(200); + + expect(reactivate.body.user.is_active).toBe(true); + }); +}); diff --git a/backend/src/tests/utils.js b/backend/src/tests/utils.js new file mode 100644 index 0000000..bc4adc3 --- /dev/null +++ b/backend/src/tests/utils.js @@ -0,0 +1,112 @@ +const { randomUUID } = require('crypto'); +const path = require('path'); +const fs = require('fs'); +const bcrypt = require('bcryptjs'); +const request = require('supertest'); +const app = require('../server'); +const config = require('../config'); +const pool = require('../database/connection'); + +const uniqueEmail = (prefix = 'user') => `${prefix}-${randomUUID()}@example.com`; + +const defaultPassword = 'Password123!'; + +async function registerUser(role = 'candidate', overrides = {}) { + const userData = { + firstName: overrides.firstName || 'Test', + lastName: overrides.lastName || 'User', + email: overrides.email || uniqueEmail(role), + password: overrides.password || defaultPassword, + role, + ...overrides + }; + + const response = await request(app) + .post('/api/auth/register') + .send(userData); + + return { + token: response.body.token, + user: response.body.user, + credentials: { + email: userData.email, + password: userData.password + } + }; +} + +async function createEmployerProfile(token, overrides = {}) { + const employerData = { + companyName: overrides.companyName || `Company ${randomUUID().slice(0, 8)}`, + industry: overrides.industry || 'Technology', + companySize: overrides.companySize || '11-50', + website: overrides.website || 'https://example.com', + description: overrides.description || 'Test company description', + address: overrides.address || '123 Test St', + phone: overrides.phone || '+1-555-0000', + ...overrides + }; + + const response = await request(app) + .post('/api/employers') + .set('Authorization', `Bearer ${token}`) + .send(employerData); + + return response.body.employer; +} + +async function createAdminUser() { + const email = uniqueEmail('admin'); + const password = defaultPassword; + const passwordHash = await bcrypt.hash(password, 10); + + const result = await pool.query( + 'INSERT INTO users (email, password_hash, first_name, last_name, role) VALUES ($1, $2, $3, $4, $5) RETURNING id', + [email, passwordHash, 'Admin', 'User', 'admin'] + ); + + const loginResponse = await request(app) + .post('/api/auth/login') + .send({ email, password }); + + return { + token: loginResponse.body.token, + user: loginResponse.body.user, + userId: result.rows[0].id, + credentials: { email, password } + }; +} + +async function ensureUploadDir() { + const uploadDir = path.join(__dirname, '..', '..', config.uploadDir); + if (!fs.existsSync(uploadDir)) { + fs.mkdirSync(uploadDir, { recursive: true }); + } + return uploadDir; +} + +async function cleanupUploads() { + const uploadDir = await ensureUploadDir(); + if (!fs.existsSync(uploadDir)) { + return; + } + const entries = fs.readdirSync(uploadDir); + for (const entry of entries) { + const filePath = path.join(uploadDir, entry); + try { + fs.unlinkSync(filePath); + } catch (error) { + // Ignore deletion errors in tests + } + } +} + +module.exports = { + registerUser, + createEmployerProfile, + createAdminUser, + uniqueEmail, + defaultPassword, + ensureUploadDir, + cleanupUploads +}; diff --git a/deploy/coolify/docker-compose.yml b/deploy/coolify/docker-compose.yml index 1f05624..eab113d 100644 --- a/deploy/coolify/docker-compose.yml +++ b/deploy/coolify/docker-compose.yml @@ -27,8 +27,11 @@ services: PORT: 3001 DATABASE_URL: postgresql://${POSTGRES_USER:-merchantsofhope}:${POSTGRES_PASSWORD}@merchantsofhope-supplyanddemandportal-database:5432/${POSTGRES_DB:-merchantsofhope_supplyanddemandportal} JWT_SECRET: ${JWT_SECRET:?set JWT_SECRET} + UPLOAD_DIR: /app/uploads/resumes ports: - "0.0.0.0:3001:3001" + volumes: + - merchantsofhope-supplyanddemandportal-uploads:/app/uploads/resumes merchantsofhope-supplyanddemandportal-frontend: image: ${FRONTEND_IMAGE:?set FRONTEND_IMAGE} @@ -44,3 +47,4 @@ services: volumes: merchantsofhope-supplyanddemandportal-postgres-data: + merchantsofhope-supplyanddemandportal-uploads: diff --git a/docker-compose.test.yml b/docker-compose.test.yml index bd7e264..c261ba5 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -8,6 +8,8 @@ services: POSTGRES_DB: merchantsofhope_test POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres + ports: + - "55432:5432" # Add a healthcheck to ensure the database is ready before tests run healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] diff --git a/docker-compose.yml b/docker-compose.yml index f90e2ad..d294a9a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,6 +29,8 @@ services: JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is not set} HOST: ${BACKEND_HOST:-0.0.0.0} PORT: ${BACKEND_PORT:-3001} + POSTGRES_HOST: merchantsofhope-supplyanddemandportal-database + UPLOAD_DIR: /app/uploads/resumes ports: - "0.0.0.0:${BACKEND_PORT:-3001}:3001" command: > @@ -38,6 +40,7 @@ services: condition: service_healthy volumes: - ./backend:/app + - backend-resume-uploads:/app/uploads/resumes - /app/node_modules networks: - merchantsofhope-supplyanddemandportal-network @@ -63,6 +66,7 @@ services: volumes: merchantsofhope-supplyanddemandportal-postgres-data: + backend-resume-uploads: networks: merchantsofhope-supplyanddemandportal-network: diff --git a/docs/COOLIFY_SANDBOX_CHECKLIST.md b/docs/COOLIFY_SANDBOX_CHECKLIST.md new file mode 100644 index 0000000..f32ad1f --- /dev/null +++ b/docs/COOLIFY_SANDBOX_CHECKLIST.md @@ -0,0 +1,74 @@ +# Coolify Sandbox Checklist + +Use this guide to spin up and maintain the disposable Coolify sandbox that mirrors production deployment flows for the MerchantsOfHope Supply & Demand Portal. + +## 1. Provision the Sandbox Host +- Choose a fresh VM/VPS with at least 2 vCPU, 4 GB RAM, and 40 GB SSD; Ubuntu 22.04 LTS is the default target. +- Allocate a dedicated subdomain (example: `coolify-sandbox.merchantsofhope.org`) and, optionally, a wildcard (`*.apps.coolify-sandbox.merchantsofhope.org`) for preview deployments. +- Open inbound ports 22/tcp (SSH), 80/443 (HTTP/HTTPS), and 8000 (initial Coolify UI before proxy is enabled). + +## 2. Prepare the OS +- Update packages: `apt update && apt upgrade -y` (adjust for distro). +- Install basics if missing: `curl`, `wget`, `git`, `jq`, `ufw`. +- Enable firewall rules that keep ports 22, 80, 443, and 8000 reachable. + +## 3. Install Coolify +- Log in as `root` (or use `sudo -E`) and run the official quick installer: + ```bash + curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash + ``` +- Create the initial admin account immediately after the installer prints the access URL. +- Optional: preseed credentials during install with `ROOT_USERNAME`, `ROOT_USER_EMAIL`, and `ROOT_USER_PASSWORD` environment variables. + +## 4. Post-Install Hardening +- Add an A record for the chosen hostname pointing at the VM; set up a wildcard record if PR previews are desired. +- In the Coolify dashboard, enable HTTPS via the bundled Traefik proxy (Let’s Encrypt). +- Change the default SSH port if desired and ensure the Coolify-generated SSH key is present at `/data/coolify/ssh/keys`. +- Configure automatic upgrades to "manual" so upgrades are rehearsed here before production. + +## 5. Connect Integrations +- **Gitea**: add a Gitea integration in Coolify (Settings → Git Sources) using a service account or personal access token with repo read access. +- **Container registry**: decide between the built-in Gitea registry or an external registry. Create a robot/service account with pull permissions for Coolify and push permissions for CI. +- **Runner location**: reuse the workstation’s runner for now; add the dedicated runner VM later if you need isolation or more concurrency. +- Store registry credentials in Coolify as a global secret group (Settings → Secrets). + +## 6. Import the Application +- Create a new *Docker Compose Application* in Coolify. +- Repository: `MerchantsOfHope-SupplyANdDemandPortal`. +- Compose path: `deploy/coolify/docker-compose.yml`. +- Define environment variables to match the Compose file: + - `BACKEND_IMAGE`, `FRONTEND_IMAGE` (full image references that CI builds and pushes). + - `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`. + - `JWT_SECRET`. + - `REACT_APP_API_URL` (use the internal backend URL for now; update to public URL after proxying). +- Configure service health checks and attach the Coolify-provided domain to the frontend service. + +## 7. Post-Deployment Hooks +- Add a hook that runs after each deploy: + ```bash + docker compose exec merchantsofhope-supplyanddemandportal-backend npm run migrate + ``` +- Optionally add a second hook for seeding: `npm run seed`. + +## 8. Validate the Pipeline +- In Gitea, set `REGISTRY_HOST`, `REGISTRY_USERNAME`, and `REGISTRY_PASSWORD` secrets so the `docker-images` job publishes images on the `main` branch. +- Run a test push to `main` (or re-run the workflow) and capture the SHA-based image tag from the workflow summary. +- Update the Coolify application’s `BACKEND_IMAGE` and `FRONTEND_IMAGE` to the new tags, then trigger a deployment. +- Verify: + - Database migrations succeed. + - Frontend loads via the Coolify-managed domain and can hit the backend. + - Logs are clean (`docker compose logs -f` in Coolify shell). + +## 9. Sandbox Maintenance +- Schedule a weekly refresh: pull the latest `main`, trigger CI, redeploy in Coolify. +- Practice backups: export Coolify configuration and snapshot the PostgreSQL volume. +- Test upgrades here before promoting to production (run the installer script to upgrade, validate, then replicate in production). +- Document any manual tweaks in this repository (`docs/INFRA_PLANNING.md` or a dedicated runbook) so production setup stays reproducible. + +## 10. Ready for Production When… +- CI → Registry → Coolify deploy loop runs without manual intervention. +- Environment variables match production values (except secrets). +- Backup and restore drills succeed in the sandbox. +- Team confirms Coolify access, deployment permissions, and preview flows. + +Track completion by checking off each section above; update this checklist with lessons learned as the sandbox evolves. diff --git a/frontend/.eslintignore b/frontend/.eslintignore new file mode 100644 index 0000000..dd7caa1 --- /dev/null +++ b/frontend/.eslintignore @@ -0,0 +1,3 @@ +node_modules +coverage +build diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js new file mode 100644 index 0000000..153100c --- /dev/null +++ b/frontend/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + extends: ['react-app', 'react-app/jest'], + rules: { + 'testing-library/no-debugging-utils': 'warn' + } +}; diff --git a/frontend/coverage/clover.xml b/frontend/coverage/clover.xml new file mode 100644 index 0000000..98cbd21 --- /dev/null +++ b/frontend/coverage/clover.xml @@ -0,0 +1,506 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/coverage/coverage-final.json b/frontend/coverage/coverage-final.json new file mode 100644 index 0000000..8778022 --- /dev/null +++ b/frontend/coverage/coverage-final.json @@ -0,0 +1,19 @@ +{"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/App.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/App.js","statementMap":{"0":{"start":{"line":21,"column":20},"end":{"line":21,"column":37}},"1":{"start":{"line":24,"column":28},"end":{"line":24,"column":37}},"2":{"start":{"line":26,"column":2},"end":{"line":32,"column":3}},"3":{"start":{"line":27,"column":4},"end":{"line":31,"column":6}},"4":{"start":{"line":34,"column":2},"end":{"line":36,"column":3}},"5":{"start":{"line":35,"column":4},"end":{"line":35,"column":44}},"6":{"start":{"line":38,"column":2},"end":{"line":40,"column":3}},"7":{"start":{"line":39,"column":4},"end":{"line":39,"column":48}},"8":{"start":{"line":42,"column":2},"end":{"line":42,"column":18}},"9":{"start":{"line":46,"column":19},"end":{"line":46,"column":28}},"10":{"start":{"line":48,"column":2},"end":{"line":118,"column":4}},"11":{"start":{"line":122,"column":2},"end":{"line":133,"column":4}}},"fnMap":{"0":{"name":"ProtectedRoute","decl":{"start":{"line":23,"column":9},"end":{"line":23,"column":23}},"loc":{"start":{"line":23,"column":57},"end":{"line":43,"column":1}},"line":23},"1":{"name":"AppRoutes","decl":{"start":{"line":45,"column":9},"end":{"line":45,"column":18}},"loc":{"start":{"line":45,"column":21},"end":{"line":119,"column":1}},"line":45},"2":{"name":"App","decl":{"start":{"line":121,"column":9},"end":{"line":121,"column":12}},"loc":{"start":{"line":121,"column":15},"end":{"line":134,"column":1}},"line":121}},"branchMap":{"0":{"loc":{"start":{"line":23,"column":36},"end":{"line":23,"column":53}},"type":"default-arg","locations":[{"start":{"line":23,"column":51},"end":{"line":23,"column":53}}],"line":23},"1":{"loc":{"start":{"line":26,"column":2},"end":{"line":32,"column":3}},"type":"if","locations":[{"start":{"line":26,"column":2},"end":{"line":32,"column":3}},{"start":{},"end":{}}],"line":26},"2":{"loc":{"start":{"line":34,"column":2},"end":{"line":36,"column":3}},"type":"if","locations":[{"start":{"line":34,"column":2},"end":{"line":36,"column":3}},{"start":{},"end":{}}],"line":34},"3":{"loc":{"start":{"line":38,"column":2},"end":{"line":40,"column":3}},"type":"if","locations":[{"start":{"line":38,"column":2},"end":{"line":40,"column":3}},{"start":{},"end":{}}],"line":38},"4":{"loc":{"start":{"line":38,"column":6},"end":{"line":38,"column":66}},"type":"binary-expr","locations":[{"start":{"line":38,"column":6},"end":{"line":38,"column":29}},{"start":{"line":38,"column":33},"end":{"line":38,"column":66}}],"line":38},"5":{"loc":{"start":{"line":50,"column":36},"end":{"line":50,"column":92}},"type":"cond-expr","locations":[{"start":{"line":50,"column":44},"end":{"line":50,"column":53}},{"start":{"line":50,"column":56},"end":{"line":50,"column":92}}],"line":50},"6":{"loc":{"start":{"line":51,"column":39},"end":{"line":51,"column":98}},"type":"cond-expr","locations":[{"start":{"line":51,"column":47},"end":{"line":51,"column":59}},{"start":{"line":51,"column":62},"end":{"line":51,"column":98}}],"line":51}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":1,"10":1,"11":1},"f":{"0":0,"1":1,"2":1},"b":{"0":[0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[1,0],"6":[1,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"0a632eede0b26125352bd1898be6f0af08e744b4"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/index.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/index.js","statementMap":{"0":{"start":{"line":7,"column":13},"end":{"line":7,"column":65}},"1":{"start":{"line":8,"column":0},"end":{"line":12,"column":2}}},"fnMap":{},"branchMap":{},"s":{"0":0,"1":0},"f":{},"b":{}} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/components/Layout.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/components/Layout.js","statementMap":{"0":{"start":{"line":17,"column":15},"end":{"line":184,"column":1}},"1":{"start":{"line":18,"column":27},"end":{"line":18,"column":36}},"2":{"start":{"line":19,"column":19},"end":{"line":19,"column":32}},"3":{"start":{"line":20,"column":40},"end":{"line":20,"column":55}},"4":{"start":{"line":22,"column":21},"end":{"line":29,"column":3}},"5":{"start":{"line":31,"column":29},"end":{"line":33,"column":3}},"6":{"start":{"line":32,"column":4},"end":{"line":32,"column":35}},"7":{"start":{"line":35,"column":23},"end":{"line":37,"column":3}},"8":{"start":{"line":36,"column":4},"end":{"line":36,"column":13}},"9":{"start":{"line":39,"column":2},"end":{"line":183,"column":4}},"10":{"start":{"line":43,"column":80},"end":{"line":43,"column":101}},"11":{"start":{"line":49,"column":29},"end":{"line":49,"column":50}},"12":{"start":{"line":60,"column":33},"end":{"line":60,"column":64}},"13":{"start":{"line":61,"column":16},"end":{"line":74,"column":18}},"14":{"start":{"line":90,"column":33},"end":{"line":90,"column":64}},"15":{"start":{"line":91,"column":16},"end":{"line":104,"column":18}},"16":{"start":{"line":131,"column":27},"end":{"line":131,"column":47}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":17,"column":15},"end":{"line":17,"column":16}},"loc":{"start":{"line":17,"column":21},"end":{"line":184,"column":1}},"line":17},"1":{"name":"(anonymous_1)","decl":{"start":{"line":31,"column":47},"end":{"line":31,"column":48}},"loc":{"start":{"line":32,"column":4},"end":{"line":32,"column":35}},"line":32},"2":{"name":"(anonymous_2)","decl":{"start":{"line":35,"column":23},"end":{"line":35,"column":24}},"loc":{"start":{"line":35,"column":29},"end":{"line":37,"column":3}},"line":35},"3":{"name":"(anonymous_3)","decl":{"start":{"line":43,"column":74},"end":{"line":43,"column":75}},"loc":{"start":{"line":43,"column":80},"end":{"line":43,"column":101}},"line":43},"4":{"name":"(anonymous_4)","decl":{"start":{"line":49,"column":23},"end":{"line":49,"column":24}},"loc":{"start":{"line":49,"column":29},"end":{"line":49,"column":50}},"line":49},"5":{"name":"(anonymous_5)","decl":{"start":{"line":59,"column":38},"end":{"line":59,"column":39}},"loc":{"start":{"line":59,"column":48},"end":{"line":75,"column":15}},"line":59},"6":{"name":"(anonymous_6)","decl":{"start":{"line":89,"column":38},"end":{"line":89,"column":39}},"loc":{"start":{"line":89,"column":48},"end":{"line":105,"column":15}},"line":89},"7":{"name":"(anonymous_7)","decl":{"start":{"line":131,"column":21},"end":{"line":131,"column":22}},"loc":{"start":{"line":131,"column":27},"end":{"line":131,"column":47}},"line":131}},"branchMap":{"0":{"loc":{"start":{"line":42,"column":54},"end":{"line":42,"column":86}},"type":"cond-expr","locations":[{"start":{"line":42,"column":68},"end":{"line":42,"column":75}},{"start":{"line":42,"column":78},"end":{"line":42,"column":86}}],"line":42},"1":{"loc":{"start":{"line":66,"column":22},"end":{"line":68,"column":78}},"type":"cond-expr","locations":[{"start":{"line":67,"column":26},"end":{"line":67,"column":59}},{"start":{"line":68,"column":26},"end":{"line":68,"column":78}}],"line":66},"2":{"loc":{"start":{"line":96,"column":22},"end":{"line":98,"column":78}},"type":"cond-expr","locations":[{"start":{"line":97,"column":26},"end":{"line":97,"column":59}},{"start":{"line":98,"column":26},"end":{"line":98,"column":78}}],"line":96}},"s":{"0":2,"1":3,"2":3,"3":3,"4":3,"5":3,"6":18,"7":3,"8":0,"9":3,"10":0,"11":0,"12":12,"13":12,"14":12,"15":12,"16":0},"f":{"0":3,"1":18,"2":0,"3":0,"4":0,"5":12,"6":12,"7":0},"b":{"0":[0,3],"1":[3,9],"2":[3,9]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"00b15d5582355db14b824199f0fc64005fa9712d"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/contexts/AuthContext.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/contexts/AuthContext.js","statementMap":{"0":{"start":{"line":5,"column":20},"end":{"line":5,"column":35}},"1":{"start":{"line":7,"column":23},"end":{"line":13,"column":1}},"2":{"start":{"line":8,"column":18},"end":{"line":8,"column":41}},"3":{"start":{"line":9,"column":2},"end":{"line":11,"column":3}},"4":{"start":{"line":10,"column":4},"end":{"line":10,"column":67}},"5":{"start":{"line":12,"column":2},"end":{"line":12,"column":17}},"6":{"start":{"line":15,"column":28},"end":{"line":105,"column":1}},"7":{"start":{"line":16,"column":26},"end":{"line":16,"column":40}},"8":{"start":{"line":17,"column":32},"end":{"line":17,"column":46}},"9":{"start":{"line":19,"column":2},"end":{"line":27,"column":9}},"10":{"start":{"line":20,"column":18},"end":{"line":20,"column":47}},"11":{"start":{"line":21,"column":4},"end":{"line":26,"column":5}},"12":{"start":{"line":22,"column":6},"end":{"line":22,"column":73}},"13":{"start":{"line":23,"column":6},"end":{"line":23,"column":18}},"14":{"start":{"line":25,"column":6},"end":{"line":25,"column":24}},"15":{"start":{"line":29,"column":20},"end":{"line":40,"column":3}},"16":{"start":{"line":30,"column":4},"end":{"line":39,"column":5}},"17":{"start":{"line":31,"column":23},"end":{"line":31,"column":54}},"18":{"start":{"line":32,"column":6},"end":{"line":32,"column":34}},"19":{"start":{"line":34,"column":6},"end":{"line":34,"column":52}},"20":{"start":{"line":35,"column":6},"end":{"line":35,"column":39}},"21":{"start":{"line":36,"column":6},"end":{"line":36,"column":60}},"22":{"start":{"line":38,"column":6},"end":{"line":38,"column":24}},"23":{"start":{"line":42,"column":16},"end":{"line":58,"column":3}},"24":{"start":{"line":43,"column":4},"end":{"line":57,"column":5}},"25":{"start":{"line":44,"column":23},"end":{"line":44,"column":79}},"26":{"start":{"line":45,"column":30},"end":{"line":45,"column":43}},"27":{"start":{"line":47,"column":6},"end":{"line":47,"column":43}},"28":{"start":{"line":48,"column":6},"end":{"line":48,"column":73}},"29":{"start":{"line":49,"column":6},"end":{"line":49,"column":20}},"30":{"start":{"line":51,"column":6},"end":{"line":51,"column":41}},"31":{"start":{"line":52,"column":6},"end":{"line":52,"column":31}},"32":{"start":{"line":54,"column":22},"end":{"line":54,"column":67}},"33":{"start":{"line":55,"column":6},"end":{"line":55,"column":27}},"34":{"start":{"line":56,"column":6},"end":{"line":56,"column":48}},"35":{"start":{"line":60,"column":19},"end":{"line":76,"column":3}},"36":{"start":{"line":61,"column":4},"end":{"line":75,"column":5}},"37":{"start":{"line":62,"column":23},"end":{"line":62,"column":71}},"38":{"start":{"line":63,"column":30},"end":{"line":63,"column":43}},"39":{"start":{"line":65,"column":6},"end":{"line":65,"column":43}},"40":{"start":{"line":66,"column":6},"end":{"line":66,"column":73}},"41":{"start":{"line":67,"column":6},"end":{"line":67,"column":20}},"42":{"start":{"line":69,"column":6},"end":{"line":69,"column":48}},"43":{"start":{"line":70,"column":6},"end":{"line":70,"column":31}},"44":{"start":{"line":72,"column":22},"end":{"line":72,"column":74}},"45":{"start":{"line":73,"column":6},"end":{"line":73,"column":27}},"46":{"start":{"line":74,"column":6},"end":{"line":74,"column":48}},"47":{"start":{"line":78,"column":17},"end":{"line":89,"column":3}},"48":{"start":{"line":79,"column":4},"end":{"line":88,"column":5}},"49":{"start":{"line":80,"column":6},"end":{"line":80,"column":43}},"50":{"start":{"line":82,"column":6},"end":{"line":82,"column":44}},"51":{"start":{"line":84,"column":6},"end":{"line":84,"column":39}},"52":{"start":{"line":85,"column":6},"end":{"line":85,"column":60}},"53":{"start":{"line":86,"column":6},"end":{"line":86,"column":20}},"54":{"start":{"line":87,"column":6},"end":{"line":87,"column":47}},"55":{"start":{"line":91,"column":16},"end":{"line":98,"column":3}},"56":{"start":{"line":100,"column":2},"end":{"line":104,"column":4}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":7,"column":23},"end":{"line":7,"column":24}},"loc":{"start":{"line":7,"column":29},"end":{"line":13,"column":1}},"line":7},"1":{"name":"(anonymous_1)","decl":{"start":{"line":15,"column":28},"end":{"line":15,"column":29}},"loc":{"start":{"line":15,"column":46},"end":{"line":105,"column":1}},"line":15},"2":{"name":"(anonymous_2)","decl":{"start":{"line":19,"column":12},"end":{"line":19,"column":13}},"loc":{"start":{"line":19,"column":18},"end":{"line":27,"column":3}},"line":19},"3":{"name":"(anonymous_3)","decl":{"start":{"line":29,"column":20},"end":{"line":29,"column":21}},"loc":{"start":{"line":29,"column":32},"end":{"line":40,"column":3}},"line":29},"4":{"name":"(anonymous_4)","decl":{"start":{"line":42,"column":16},"end":{"line":42,"column":17}},"loc":{"start":{"line":42,"column":43},"end":{"line":58,"column":3}},"line":42},"5":{"name":"(anonymous_5)","decl":{"start":{"line":60,"column":19},"end":{"line":60,"column":20}},"loc":{"start":{"line":60,"column":39},"end":{"line":76,"column":3}},"line":60},"6":{"name":"(anonymous_6)","decl":{"start":{"line":78,"column":17},"end":{"line":78,"column":18}},"loc":{"start":{"line":78,"column":29},"end":{"line":89,"column":3}},"line":78}},"branchMap":{"0":{"loc":{"start":{"line":9,"column":2},"end":{"line":11,"column":3}},"type":"if","locations":[{"start":{"line":9,"column":2},"end":{"line":11,"column":3}},{"start":{},"end":{}}],"line":9},"1":{"loc":{"start":{"line":21,"column":4},"end":{"line":26,"column":5}},"type":"if","locations":[{"start":{"line":21,"column":4},"end":{"line":26,"column":5}},{"start":{"line":24,"column":11},"end":{"line":26,"column":5}}],"line":21},"2":{"loc":{"start":{"line":54,"column":22},"end":{"line":54,"column":67}},"type":"binary-expr","locations":[{"start":{"line":54,"column":22},"end":{"line":54,"column":49}},{"start":{"line":54,"column":53},"end":{"line":54,"column":67}}],"line":54},"3":{"loc":{"start":{"line":72,"column":22},"end":{"line":72,"column":74}},"type":"binary-expr","locations":[{"start":{"line":72,"column":22},"end":{"line":72,"column":49}},{"start":{"line":72,"column":53},"end":{"line":72,"column":74}}],"line":72}},"s":{"0":1,"1":1,"2":9,"3":9,"4":0,"5":9,"6":1,"7":9,"8":9,"9":9,"10":3,"11":3,"12":0,"13":0,"14":3,"15":9,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":9,"24":3,"25":3,"26":2,"27":2,"28":2,"29":2,"30":2,"31":2,"32":1,"33":1,"34":1,"35":9,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":9,"48":1,"49":1,"50":0,"51":1,"52":1,"53":1,"54":1,"55":9,"56":9},"f":{"0":9,"1":9,"2":3,"3":0,"4":3,"5":0,"6":1},"b":{"0":[0,9],"1":[0,3],"2":[1,0],"3":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"3c393c6f3d909f534cb3481e4b46f516aa541d4e"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/lib/configureAxios.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/lib/configureAxios.js","statementMap":{"0":{"start":{"line":3,"column":16},"end":{"line":3,"column":51}},"1":{"start":{"line":5,"column":0},"end":{"line":7,"column":1}},"2":{"start":{"line":6,"column":2},"end":{"line":6,"column":35}},"3":{"start":{"line":9,"column":0},"end":{"line":9,"column":67}}},"fnMap":{},"branchMap":{"0":{"loc":{"start":{"line":3,"column":16},"end":{"line":3,"column":51}},"type":"binary-expr","locations":[{"start":{"line":3,"column":16},"end":{"line":3,"column":45}},{"start":{"line":3,"column":49},"end":{"line":3,"column":51}}],"line":3},"1":{"loc":{"start":{"line":5,"column":0},"end":{"line":7,"column":1}},"type":"if","locations":[{"start":{"line":5,"column":0},"end":{"line":7,"column":1}},{"start":{},"end":{}}],"line":5}},"s":{"0":0,"1":0,"2":0,"3":0},"f":{},"b":{"0":[0,0],"1":[0,0]}} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Applications.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Applications.js","statementMap":{"0":{"start":{"line":6,"column":21},"end":{"line":109,"column":1}},"1":{"start":{"line":7,"column":30},"end":{"line":10,"column":4}},"2":{"start":{"line":8,"column":21},"end":{"line":8,"column":57}},"3":{"start":{"line":9,"column":4},"end":{"line":9,"column":25}},"4":{"start":{"line":12,"column":24},"end":{"line":22,"column":3}},"5":{"start":{"line":13,"column":4},"end":{"line":21,"column":5}},"6":{"start":{"line":14,"column":22},"end":{"line":14,"column":59}},"7":{"start":{"line":15,"column":23},"end":{"line":15,"column":66}},"8":{"start":{"line":16,"column":26},"end":{"line":16,"column":69}},"9":{"start":{"line":17,"column":26},"end":{"line":17,"column":69}},"10":{"start":{"line":18,"column":22},"end":{"line":18,"column":65}},"11":{"start":{"line":19,"column":23},"end":{"line":19,"column":62}},"12":{"start":{"line":20,"column":15},"end":{"line":20,"column":52}},"13":{"start":{"line":24,"column":25},"end":{"line":35,"column":3}},"14":{"start":{"line":25,"column":4},"end":{"line":34,"column":5}},"15":{"start":{"line":26,"column":22},"end":{"line":26,"column":57}},"16":{"start":{"line":27,"column":23},"end":{"line":27,"column":62}},"17":{"start":{"line":28,"column":26},"end":{"line":28,"column":63}},"18":{"start":{"line":29,"column":26},"end":{"line":29,"column":63}},"19":{"start":{"line":30,"column":22},"end":{"line":30,"column":59}},"20":{"start":{"line":31,"column":23},"end":{"line":31,"column":56}},"21":{"start":{"line":32,"column":24},"end":{"line":32,"column":59}},"22":{"start":{"line":33,"column":15},"end":{"line":33,"column":50}},"23":{"start":{"line":37,"column":2},"end":{"line":43,"column":3}},"24":{"start":{"line":38,"column":4},"end":{"line":42,"column":6}},"25":{"start":{"line":45,"column":2},"end":{"line":108,"column":4}},"26":{"start":{"line":57,"column":12},"end":{"line":95,"column":18}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":21},"end":{"line":6,"column":22}},"loc":{"start":{"line":6,"column":27},"end":{"line":109,"column":1}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":7,"column":55},"end":{"line":7,"column":56}},"loc":{"start":{"line":7,"column":67},"end":{"line":10,"column":3}},"line":7},"2":{"name":"(anonymous_2)","decl":{"start":{"line":12,"column":24},"end":{"line":12,"column":25}},"loc":{"start":{"line":12,"column":36},"end":{"line":22,"column":3}},"line":12},"3":{"name":"(anonymous_3)","decl":{"start":{"line":24,"column":25},"end":{"line":24,"column":26}},"loc":{"start":{"line":24,"column":37},"end":{"line":35,"column":3}},"line":24},"4":{"name":"(anonymous_4)","decl":{"start":{"line":56,"column":32},"end":{"line":56,"column":33}},"loc":{"start":{"line":57,"column":12},"end":{"line":95,"column":18}},"line":57}},"branchMap":{"0":{"loc":{"start":{"line":13,"column":4},"end":{"line":21,"column":5}},"type":"switch","locations":[{"start":{"line":14,"column":6},"end":{"line":14,"column":59}},{"start":{"line":15,"column":6},"end":{"line":15,"column":66}},{"start":{"line":16,"column":6},"end":{"line":16,"column":69}},{"start":{"line":17,"column":6},"end":{"line":17,"column":69}},{"start":{"line":18,"column":6},"end":{"line":18,"column":65}},{"start":{"line":19,"column":6},"end":{"line":19,"column":62}},{"start":{"line":20,"column":6},"end":{"line":20,"column":52}}],"line":13},"1":{"loc":{"start":{"line":25,"column":4},"end":{"line":34,"column":5}},"type":"switch","locations":[{"start":{"line":26,"column":6},"end":{"line":26,"column":57}},{"start":{"line":27,"column":6},"end":{"line":27,"column":62}},{"start":{"line":28,"column":6},"end":{"line":28,"column":63}},{"start":{"line":29,"column":6},"end":{"line":29,"column":63}},{"start":{"line":30,"column":6},"end":{"line":30,"column":59}},{"start":{"line":31,"column":6},"end":{"line":31,"column":56}},{"start":{"line":32,"column":6},"end":{"line":32,"column":59}},{"start":{"line":33,"column":6},"end":{"line":33,"column":50}}],"line":25},"2":{"loc":{"start":{"line":37,"column":2},"end":{"line":43,"column":3}},"type":"if","locations":[{"start":{"line":37,"column":2},"end":{"line":43,"column":3}},{"start":{},"end":{}}],"line":37},"3":{"loc":{"start":{"line":55,"column":9},"end":{"line":105,"column":9}},"type":"cond-expr","locations":[{"start":{"line":56,"column":10},"end":{"line":96,"column":12}},{"start":{"line":98,"column":10},"end":{"line":104,"column":16}}],"line":55},"4":{"loc":{"start":{"line":78,"column":21},"end":{"line":84,"column":21}},"type":"binary-expr","locations":[{"start":{"line":78,"column":21},"end":{"line":78,"column":45}},{"start":{"line":79,"column":22},"end":{"line":83,"column":28}}],"line":78},"5":{"loc":{"start":{"line":85,"column":21},"end":{"line":91,"column":21}},"type":"binary-expr","locations":[{"start":{"line":85,"column":21},"end":{"line":85,"column":38}},{"start":{"line":86,"column":22},"end":{"line":90,"column":28}}],"line":85}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0},"b":{"0":[0,0,0,0,0,0,0],"1":[0,0,0,0,0,0,0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"16e6629018b45c03e9f6736a041581e31833450f"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/CandidateDetails.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/CandidateDetails.js","statementMap":{"0":{"start":{"line":7,"column":25},"end":{"line":147,"column":1}},"1":{"start":{"line":8,"column":17},"end":{"line":8,"column":28}},"2":{"start":{"line":10,"column":41},"end":{"line":13,"column":4}},"3":{"start":{"line":11,"column":21},"end":{"line":11,"column":61}},"4":{"start":{"line":12,"column":4},"end":{"line":12,"column":25}},"5":{"start":{"line":15,"column":2},"end":{"line":21,"column":3}},"6":{"start":{"line":16,"column":4},"end":{"line":20,"column":6}},"7":{"start":{"line":23,"column":2},"end":{"line":32,"column":3}},"8":{"start":{"line":24,"column":4},"end":{"line":31,"column":6}},"9":{"start":{"line":34,"column":2},"end":{"line":146,"column":4}},"10":{"start":{"line":112,"column":16},"end":{"line":117,"column":23}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":7,"column":25},"end":{"line":7,"column":26}},"loc":{"start":{"line":7,"column":31},"end":{"line":147,"column":1}},"line":7},"1":{"name":"(anonymous_1)","decl":{"start":{"line":10,"column":69},"end":{"line":10,"column":70}},"loc":{"start":{"line":10,"column":81},"end":{"line":13,"column":3}},"line":10},"2":{"name":"(anonymous_2)","decl":{"start":{"line":111,"column":36},"end":{"line":111,"column":37}},"loc":{"start":{"line":112,"column":16},"end":{"line":117,"column":23}},"line":112}},"branchMap":{"0":{"loc":{"start":{"line":15,"column":2},"end":{"line":21,"column":3}},"type":"if","locations":[{"start":{"line":15,"column":2},"end":{"line":21,"column":3}},{"start":{},"end":{}}],"line":15},"1":{"loc":{"start":{"line":23,"column":2},"end":{"line":32,"column":3}},"type":"if","locations":[{"start":{"line":23,"column":2},"end":{"line":32,"column":3}},{"start":{},"end":{}}],"line":23},"2":{"loc":{"start":{"line":51,"column":17},"end":{"line":56,"column":17}},"type":"binary-expr","locations":[{"start":{"line":51,"column":17},"end":{"line":51,"column":35}},{"start":{"line":52,"column":18},"end":{"line":55,"column":24}}],"line":51},"3":{"loc":{"start":{"line":58,"column":17},"end":{"line":63,"column":17}},"type":"binary-expr","locations":[{"start":{"line":58,"column":17},"end":{"line":58,"column":32}},{"start":{"line":59,"column":18},"end":{"line":62,"column":24}}],"line":58},"4":{"loc":{"start":{"line":65,"column":17},"end":{"line":72,"column":17}},"type":"binary-expr","locations":[{"start":{"line":65,"column":17},"end":{"line":65,"column":39}},{"start":{"line":66,"column":18},"end":{"line":71,"column":24}}],"line":65},"5":{"loc":{"start":{"line":74,"column":17},"end":{"line":81,"column":17}},"type":"binary-expr","locations":[{"start":{"line":74,"column":17},"end":{"line":74,"column":37}},{"start":{"line":75,"column":18},"end":{"line":80,"column":24}}],"line":74},"6":{"loc":{"start":{"line":83,"column":17},"end":{"line":90,"column":17}},"type":"binary-expr","locations":[{"start":{"line":83,"column":17},"end":{"line":83,"column":40}},{"start":{"line":84,"column":18},"end":{"line":89,"column":24}}],"line":83},"7":{"loc":{"start":{"line":97,"column":7},"end":{"line":104,"column":7}},"type":"binary-expr","locations":[{"start":{"line":97,"column":7},"end":{"line":97,"column":20}},{"start":{"line":98,"column":8},"end":{"line":103,"column":14}}],"line":97},"8":{"loc":{"start":{"line":106,"column":7},"end":{"line":122,"column":7}},"type":"binary-expr","locations":[{"start":{"line":106,"column":7},"end":{"line":106,"column":23}},{"start":{"line":106,"column":27},"end":{"line":106,"column":54}},{"start":{"line":107,"column":8},"end":{"line":121,"column":14}}],"line":106},"9":{"loc":{"start":{"line":129,"column":15},"end":{"line":129,"column":60}},"type":"binary-expr","locations":[{"start":{"line":129,"column":15},"end":{"line":129,"column":41}},{"start":{"line":129,"column":45},"end":{"line":129,"column":60}}],"line":129},"10":{"loc":{"start":{"line":134,"column":9},"end":{"line":143,"column":9}},"type":"binary-expr","locations":[{"start":{"line":134,"column":9},"end":{"line":134,"column":37}},{"start":{"line":135,"column":10},"end":{"line":142,"column":16}}],"line":134}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0},"f":{"0":0,"1":0,"2":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0,0],"9":[0,0],"10":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"32e298fbdcdadb63aa2d368aa463bf43d4528c9a"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Candidates.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Candidates.js","statementMap":{"0":{"start":{"line":7,"column":19},"end":{"line":275,"column":1}},"1":{"start":{"line":8,"column":32},"end":{"line":13,"column":4}},"2":{"start":{"line":15,"column":39},"end":{"line":23,"column":4}},"3":{"start":{"line":16,"column":19},"end":{"line":16,"column":40}},"4":{"start":{"line":17,"column":4},"end":{"line":19,"column":7}},"5":{"start":{"line":18,"column":6},"end":{"line":18,"column":43}},"6":{"start":{"line":18,"column":17},"end":{"line":18,"column":43}},"7":{"start":{"line":21,"column":21},"end":{"line":21,"column":76}},"8":{"start":{"line":22,"column":4},"end":{"line":22,"column":25}},"9":{"start":{"line":25,"column":29},"end":{"line":30,"column":3}},"10":{"start":{"line":26,"column":4},"end":{"line":29,"column":7}},"11":{"start":{"line":32,"column":23},"end":{"line":35,"column":3}},"12":{"start":{"line":33,"column":4},"end":{"line":33,"column":23}},"13":{"start":{"line":34,"column":4},"end":{"line":34,"column":14}},"14":{"start":{"line":37,"column":29},"end":{"line":46,"column":3}},"15":{"start":{"line":38,"column":4},"end":{"line":45,"column":5}},"16":{"start":{"line":39,"column":20},"end":{"line":39,"column":57}},"17":{"start":{"line":40,"column":18},"end":{"line":40,"column":53}},"18":{"start":{"line":41,"column":21},"end":{"line":41,"column":60}},"19":{"start":{"line":42,"column":19},"end":{"line":42,"column":58}},"20":{"start":{"line":43,"column":24},"end":{"line":43,"column":57}},"21":{"start":{"line":44,"column":15},"end":{"line":44,"column":50}},"22":{"start":{"line":48,"column":2},"end":{"line":54,"column":3}},"23":{"start":{"line":49,"column":4},"end":{"line":53,"column":6}},"24":{"start":{"line":56,"column":2},"end":{"line":274,"column":4}},"25":{"start":{"line":162,"column":12},"end":{"line":230,"column":18}},"26":{"start":{"line":205,"column":28},"end":{"line":210,"column":35}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":7,"column":19},"end":{"line":7,"column":20}},"loc":{"start":{"line":7,"column":25},"end":{"line":275,"column":1}},"line":7},"1":{"name":"(anonymous_1)","decl":{"start":{"line":15,"column":73},"end":{"line":15,"column":74}},"loc":{"start":{"line":15,"column":85},"end":{"line":23,"column":3}},"line":15},"2":{"name":"(anonymous_2)","decl":{"start":{"line":17,"column":36},"end":{"line":17,"column":37}},"loc":{"start":{"line":17,"column":54},"end":{"line":19,"column":5}},"line":17},"3":{"name":"(anonymous_3)","decl":{"start":{"line":25,"column":29},"end":{"line":25,"column":30}},"loc":{"start":{"line":25,"column":36},"end":{"line":30,"column":3}},"line":25},"4":{"name":"(anonymous_4)","decl":{"start":{"line":32,"column":23},"end":{"line":32,"column":24}},"loc":{"start":{"line":32,"column":30},"end":{"line":35,"column":3}},"line":32},"5":{"name":"(anonymous_5)","decl":{"start":{"line":37,"column":29},"end":{"line":37,"column":30}},"loc":{"start":{"line":37,"column":40},"end":{"line":46,"column":3}},"line":37},"6":{"name":"(anonymous_6)","decl":{"start":{"line":161,"column":30},"end":{"line":161,"column":31}},"loc":{"start":{"line":162,"column":12},"end":{"line":230,"column":18}},"line":162},"7":{"name":"(anonymous_7)","decl":{"start":{"line":204,"column":60},"end":{"line":204,"column":61}},"loc":{"start":{"line":205,"column":28},"end":{"line":210,"column":35}},"line":205}},"branchMap":{"0":{"loc":{"start":{"line":18,"column":6},"end":{"line":18,"column":43}},"type":"if","locations":[{"start":{"line":18,"column":6},"end":{"line":18,"column":43}},{"start":{},"end":{}}],"line":18},"1":{"loc":{"start":{"line":38,"column":4},"end":{"line":45,"column":5}},"type":"switch","locations":[{"start":{"line":39,"column":6},"end":{"line":39,"column":57}},{"start":{"line":40,"column":6},"end":{"line":40,"column":53}},{"start":{"line":41,"column":6},"end":{"line":41,"column":60}},{"start":{"line":42,"column":6},"end":{"line":42,"column":58}},{"start":{"line":43,"column":6},"end":{"line":43,"column":57}},{"start":{"line":44,"column":6},"end":{"line":44,"column":50}}],"line":38},"2":{"loc":{"start":{"line":48,"column":2},"end":{"line":54,"column":3}},"type":"if","locations":[{"start":{"line":48,"column":2},"end":{"line":54,"column":3}},{"start":{},"end":{}}],"line":48},"3":{"loc":{"start":{"line":160,"column":9},"end":{"line":240,"column":9}},"type":"cond-expr","locations":[{"start":{"line":161,"column":10},"end":{"line":231,"column":12}},{"start":{"line":233,"column":10},"end":{"line":239,"column":16}}],"line":160},"4":{"loc":{"start":{"line":178,"column":21},"end":{"line":183,"column":21}},"type":"binary-expr","locations":[{"start":{"line":178,"column":21},"end":{"line":178,"column":39}},{"start":{"line":179,"column":22},"end":{"line":182,"column":28}}],"line":178},"5":{"loc":{"start":{"line":185,"column":21},"end":{"line":191,"column":21}},"type":"binary-expr","locations":[{"start":{"line":185,"column":21},"end":{"line":185,"column":47}},{"start":{"line":186,"column":22},"end":{"line":190,"column":28}}],"line":185},"6":{"loc":{"start":{"line":193,"column":21},"end":{"line":199,"column":21}},"type":"binary-expr","locations":[{"start":{"line":193,"column":21},"end":{"line":193,"column":34}},{"start":{"line":194,"column":22},"end":{"line":198,"column":28}}],"line":193},"7":{"loc":{"start":{"line":201,"column":21},"end":{"line":219,"column":21}},"type":"binary-expr","locations":[{"start":{"line":201,"column":21},"end":{"line":201,"column":37}},{"start":{"line":201,"column":41},"end":{"line":201,"column":68}},{"start":{"line":202,"column":22},"end":{"line":218,"column":28}}],"line":201},"8":{"loc":{"start":{"line":212,"column":27},"end":{"line":216,"column":27}},"type":"binary-expr","locations":[{"start":{"line":212,"column":27},"end":{"line":212,"column":54}},{"start":{"line":213,"column":28},"end":{"line":215,"column":35}}],"line":212},"9":{"loc":{"start":{"line":221,"column":21},"end":{"line":226,"column":21}},"type":"binary-expr","locations":[{"start":{"line":221,"column":21},"end":{"line":221,"column":49}},{"start":{"line":222,"column":22},"end":{"line":225,"column":28}}],"line":221},"10":{"loc":{"start":{"line":244,"column":7},"end":{"line":272,"column":7}},"type":"binary-expr","locations":[{"start":{"line":244,"column":7},"end":{"line":244,"column":23}},{"start":{"line":244,"column":27},"end":{"line":244,"column":52}},{"start":{"line":245,"column":8},"end":{"line":271,"column":14}}],"line":244}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0},"b":{"0":[0,0],"1":[0,0,0,0,0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0,0],"8":[0,0],"9":[0,0],"10":[0,0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"9c703e98ff46395e8ae1a963e82edccbfb2e66b2"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/CreateJob.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/CreateJob.js","statementMap":{"0":{"start":{"line":8,"column":18},"end":{"line":460,"column":1}},"1":{"start":{"line":9,"column":19},"end":{"line":9,"column":32}},"2":{"start":{"line":10,"column":34},"end":{"line":25,"column":4}},"3":{"start":{"line":27,"column":28},"end":{"line":38,"column":4}},"4":{"start":{"line":28,"column":21},"end":{"line":28,"column":59}},"5":{"start":{"line":29,"column":4},"end":{"line":29,"column":25}},"6":{"start":{"line":32,"column":6},"end":{"line":32,"column":48}},"7":{"start":{"line":33,"column":6},"end":{"line":33,"column":24}},"8":{"start":{"line":36,"column":6},"end":{"line":36,"column":73}},"9":{"start":{"line":40,"column":23},"end":{"line":46,"column":3}},"10":{"start":{"line":41,"column":43},"end":{"line":41,"column":51}},"11":{"start":{"line":42,"column":4},"end":{"line":45,"column":8}},"12":{"start":{"line":42,"column":25},"end":{"line":45,"column":5}},"13":{"start":{"line":48,"column":28},"end":{"line":53,"column":3}},"14":{"start":{"line":49,"column":4},"end":{"line":52,"column":8}},"15":{"start":{"line":49,"column":25},"end":{"line":52,"column":5}},"16":{"start":{"line":51,"column":44},"end":{"line":51,"column":70}},"17":{"start":{"line":55,"column":23},"end":{"line":60,"column":3}},"18":{"start":{"line":56,"column":4},"end":{"line":59,"column":8}},"19":{"start":{"line":56,"column":25},"end":{"line":59,"column":5}},"20":{"start":{"line":62,"column":26},"end":{"line":67,"column":3}},"21":{"start":{"line":63,"column":4},"end":{"line":66,"column":8}},"22":{"start":{"line":63,"column":25},"end":{"line":66,"column":5}},"23":{"start":{"line":65,"column":44},"end":{"line":65,"column":55}},"24":{"start":{"line":69,"column":23},"end":{"line":85,"column":3}},"25":{"start":{"line":70,"column":4},"end":{"line":70,"column":23}},"26":{"start":{"line":73,"column":24},"end":{"line":82,"column":5}},"27":{"start":{"line":75,"column":56},"end":{"line":75,"column":66}},"28":{"start":{"line":76,"column":65},"end":{"line":76,"column":76}},"29":{"start":{"line":77,"column":62},"end":{"line":77,"column":74}},"30":{"start":{"line":78,"column":52},"end":{"line":78,"column":66}},"31":{"start":{"line":84,"column":4},"end":{"line":84,"column":42}},"32":{"start":{"line":87,"column":2},"end":{"line":459,"column":4}},"33":{"start":{"line":91,"column":25},"end":{"line":91,"column":42}},"34":{"start":{"line":191,"column":18},"end":{"line":208,"column":24}},"35":{"start":{"line":196,"column":39},"end":{"line":196,"column":95}},"36":{"start":{"line":202,"column":39},"end":{"line":202,"column":77}},"37":{"start":{"line":212,"column":33},"end":{"line":212,"column":61}},"38":{"start":{"line":225,"column":18},"end":{"line":242,"column":24}},"39":{"start":{"line":230,"column":39},"end":{"line":230,"column":99}},"40":{"start":{"line":236,"column":39},"end":{"line":236,"column":81}},"41":{"start":{"line":246,"column":33},"end":{"line":246,"column":65}},"42":{"start":{"line":373,"column":18},"end":{"line":390,"column":24}},"43":{"start":{"line":378,"column":39},"end":{"line":378,"column":97}},"44":{"start":{"line":384,"column":39},"end":{"line":384,"column":79}},"45":{"start":{"line":394,"column":33},"end":{"line":394,"column":63}},"46":{"start":{"line":407,"column":18},"end":{"line":424,"column":24}},"47":{"start":{"line":412,"column":39},"end":{"line":412,"column":91}},"48":{"start":{"line":418,"column":39},"end":{"line":418,"column":73}},"49":{"start":{"line":428,"column":33},"end":{"line":428,"column":57}},"50":{"start":{"line":443,"column":27},"end":{"line":443,"column":44}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":8,"column":18},"end":{"line":8,"column":19}},"loc":{"start":{"line":8,"column":24},"end":{"line":460,"column":1}},"line":8},"1":{"name":"(anonymous_1)","decl":{"start":{"line":27,"column":40},"end":{"line":27,"column":41}},"loc":{"start":{"line":27,"column":59},"end":{"line":30,"column":3}},"line":27},"2":{"name":"(anonymous_2)","decl":{"start":{"line":31,"column":15},"end":{"line":31,"column":16}},"loc":{"start":{"line":31,"column":21},"end":{"line":34,"column":5}},"line":31},"3":{"name":"(anonymous_3)","decl":{"start":{"line":35,"column":13},"end":{"line":35,"column":14}},"loc":{"start":{"line":35,"column":24},"end":{"line":37,"column":5}},"line":35},"4":{"name":"(anonymous_4)","decl":{"start":{"line":40,"column":23},"end":{"line":40,"column":24}},"loc":{"start":{"line":40,"column":30},"end":{"line":46,"column":3}},"line":40},"5":{"name":"(anonymous_5)","decl":{"start":{"line":42,"column":16},"end":{"line":42,"column":17}},"loc":{"start":{"line":42,"column":25},"end":{"line":45,"column":5}},"line":42},"6":{"name":"(anonymous_6)","decl":{"start":{"line":48,"column":28},"end":{"line":48,"column":29}},"loc":{"start":{"line":48,"column":53},"end":{"line":53,"column":3}},"line":48},"7":{"name":"(anonymous_7)","decl":{"start":{"line":49,"column":16},"end":{"line":49,"column":17}},"loc":{"start":{"line":49,"column":25},"end":{"line":52,"column":5}},"line":49},"8":{"name":"(anonymous_8)","decl":{"start":{"line":51,"column":31},"end":{"line":51,"column":32}},"loc":{"start":{"line":51,"column":44},"end":{"line":51,"column":70}},"line":51},"9":{"name":"(anonymous_9)","decl":{"start":{"line":55,"column":23},"end":{"line":55,"column":24}},"loc":{"start":{"line":55,"column":34},"end":{"line":60,"column":3}},"line":55},"10":{"name":"(anonymous_10)","decl":{"start":{"line":56,"column":16},"end":{"line":56,"column":17}},"loc":{"start":{"line":56,"column":25},"end":{"line":59,"column":5}},"line":56},"11":{"name":"(anonymous_11)","decl":{"start":{"line":62,"column":26},"end":{"line":62,"column":27}},"loc":{"start":{"line":62,"column":44},"end":{"line":67,"column":3}},"line":62},"12":{"name":"(anonymous_12)","decl":{"start":{"line":63,"column":16},"end":{"line":63,"column":17}},"loc":{"start":{"line":63,"column":25},"end":{"line":66,"column":5}},"line":63},"13":{"name":"(anonymous_13)","decl":{"start":{"line":65,"column":34},"end":{"line":65,"column":35}},"loc":{"start":{"line":65,"column":44},"end":{"line":65,"column":55}},"line":65},"14":{"name":"(anonymous_14)","decl":{"start":{"line":69,"column":23},"end":{"line":69,"column":24}},"loc":{"start":{"line":69,"column":30},"end":{"line":85,"column":3}},"line":69},"15":{"name":"(anonymous_15)","decl":{"start":{"line":75,"column":49},"end":{"line":75,"column":50}},"loc":{"start":{"line":75,"column":56},"end":{"line":75,"column":66}},"line":75},"16":{"name":"(anonymous_16)","decl":{"start":{"line":76,"column":57},"end":{"line":76,"column":58}},"loc":{"start":{"line":76,"column":65},"end":{"line":76,"column":76}},"line":76},"17":{"name":"(anonymous_17)","decl":{"start":{"line":77,"column":53},"end":{"line":77,"column":54}},"loc":{"start":{"line":77,"column":62},"end":{"line":77,"column":74}},"line":77},"18":{"name":"(anonymous_18)","decl":{"start":{"line":78,"column":41},"end":{"line":78,"column":42}},"loc":{"start":{"line":78,"column":52},"end":{"line":78,"column":66}},"line":78},"19":{"name":"(anonymous_19)","decl":{"start":{"line":91,"column":19},"end":{"line":91,"column":20}},"loc":{"start":{"line":91,"column":25},"end":{"line":91,"column":42}},"line":91},"20":{"name":"(anonymous_20)","decl":{"start":{"line":190,"column":43},"end":{"line":190,"column":44}},"loc":{"start":{"line":191,"column":18},"end":{"line":208,"column":24}},"line":191},"21":{"name":"(anonymous_21)","decl":{"start":{"line":196,"column":32},"end":{"line":196,"column":33}},"loc":{"start":{"line":196,"column":39},"end":{"line":196,"column":95}},"line":196},"22":{"name":"(anonymous_22)","decl":{"start":{"line":202,"column":33},"end":{"line":202,"column":34}},"loc":{"start":{"line":202,"column":39},"end":{"line":202,"column":77}},"line":202},"23":{"name":"(anonymous_23)","decl":{"start":{"line":212,"column":27},"end":{"line":212,"column":28}},"loc":{"start":{"line":212,"column":33},"end":{"line":212,"column":61}},"line":212},"24":{"name":"(anonymous_24)","decl":{"start":{"line":224,"column":47},"end":{"line":224,"column":48}},"loc":{"start":{"line":225,"column":18},"end":{"line":242,"column":24}},"line":225},"25":{"name":"(anonymous_25)","decl":{"start":{"line":230,"column":32},"end":{"line":230,"column":33}},"loc":{"start":{"line":230,"column":39},"end":{"line":230,"column":99}},"line":230},"26":{"name":"(anonymous_26)","decl":{"start":{"line":236,"column":33},"end":{"line":236,"column":34}},"loc":{"start":{"line":236,"column":39},"end":{"line":236,"column":81}},"line":236},"27":{"name":"(anonymous_27)","decl":{"start":{"line":246,"column":27},"end":{"line":246,"column":28}},"loc":{"start":{"line":246,"column":33},"end":{"line":246,"column":65}},"line":246},"28":{"name":"(anonymous_28)","decl":{"start":{"line":372,"column":45},"end":{"line":372,"column":46}},"loc":{"start":{"line":373,"column":18},"end":{"line":390,"column":24}},"line":373},"29":{"name":"(anonymous_29)","decl":{"start":{"line":378,"column":32},"end":{"line":378,"column":33}},"loc":{"start":{"line":378,"column":39},"end":{"line":378,"column":97}},"line":378},"30":{"name":"(anonymous_30)","decl":{"start":{"line":384,"column":33},"end":{"line":384,"column":34}},"loc":{"start":{"line":384,"column":39},"end":{"line":384,"column":79}},"line":384},"31":{"name":"(anonymous_31)","decl":{"start":{"line":394,"column":27},"end":{"line":394,"column":28}},"loc":{"start":{"line":394,"column":33},"end":{"line":394,"column":63}},"line":394},"32":{"name":"(anonymous_32)","decl":{"start":{"line":406,"column":39},"end":{"line":406,"column":40}},"loc":{"start":{"line":407,"column":18},"end":{"line":424,"column":24}},"line":407},"33":{"name":"(anonymous_33)","decl":{"start":{"line":412,"column":32},"end":{"line":412,"column":33}},"loc":{"start":{"line":412,"column":39},"end":{"line":412,"column":91}},"line":412},"34":{"name":"(anonymous_34)","decl":{"start":{"line":418,"column":33},"end":{"line":418,"column":34}},"loc":{"start":{"line":418,"column":39},"end":{"line":418,"column":73}},"line":418},"35":{"name":"(anonymous_35)","decl":{"start":{"line":428,"column":27},"end":{"line":428,"column":28}},"loc":{"start":{"line":428,"column":33},"end":{"line":428,"column":57}},"line":428},"36":{"name":"(anonymous_36)","decl":{"start":{"line":443,"column":21},"end":{"line":443,"column":22}},"loc":{"start":{"line":443,"column":27},"end":{"line":443,"column":44}},"line":443}},"branchMap":{"0":{"loc":{"start":{"line":36,"column":18},"end":{"line":36,"column":71}},"type":"binary-expr","locations":[{"start":{"line":36,"column":18},"end":{"line":36,"column":45}},{"start":{"line":36,"column":49},"end":{"line":36,"column":71}}],"line":36},"1":{"loc":{"start":{"line":44,"column":14},"end":{"line":44,"column":51}},"type":"cond-expr","locations":[{"start":{"line":44,"column":36},"end":{"line":44,"column":43}},{"start":{"line":44,"column":46},"end":{"line":44,"column":51}}],"line":44},"2":{"loc":{"start":{"line":51,"column":44},"end":{"line":51,"column":70}},"type":"cond-expr","locations":[{"start":{"line":51,"column":58},"end":{"line":51,"column":63}},{"start":{"line":51,"column":66},"end":{"line":51,"column":70}}],"line":51},"3":{"loc":{"start":{"line":79,"column":17},"end":{"line":79,"column":78}},"type":"cond-expr","locations":[{"start":{"line":79,"column":38},"end":{"line":79,"column":66}},{"start":{"line":79,"column":69},"end":{"line":79,"column":78}}],"line":79},"4":{"loc":{"start":{"line":80,"column":17},"end":{"line":80,"column":78}},"type":"cond-expr","locations":[{"start":{"line":80,"column":38},"end":{"line":80,"column":66}},{"start":{"line":80,"column":69},"end":{"line":80,"column":78}}],"line":80},"5":{"loc":{"start":{"line":81,"column":27},"end":{"line":81,"column":68}},"type":"binary-expr","locations":[{"start":{"line":81,"column":27},"end":{"line":81,"column":55}},{"start":{"line":81,"column":59},"end":{"line":81,"column":68}}],"line":81},"6":{"loc":{"start":{"line":199,"column":21},"end":{"line":207,"column":21}},"type":"binary-expr","locations":[{"start":{"line":199,"column":21},"end":{"line":199,"column":53}},{"start":{"line":200,"column":22},"end":{"line":206,"column":31}}],"line":199},"7":{"loc":{"start":{"line":233,"column":21},"end":{"line":241,"column":21}},"type":"binary-expr","locations":[{"start":{"line":233,"column":21},"end":{"line":233,"column":57}},{"start":{"line":234,"column":22},"end":{"line":240,"column":31}}],"line":233},"8":{"loc":{"start":{"line":381,"column":21},"end":{"line":389,"column":21}},"type":"binary-expr","locations":[{"start":{"line":381,"column":21},"end":{"line":381,"column":55}},{"start":{"line":382,"column":22},"end":{"line":388,"column":31}}],"line":381},"9":{"loc":{"start":{"line":415,"column":21},"end":{"line":423,"column":21}},"type":"binary-expr","locations":[{"start":{"line":415,"column":21},"end":{"line":415,"column":49}},{"start":{"line":416,"column":22},"end":{"line":422,"column":31}}],"line":415},"10":{"loc":{"start":{"line":454,"column":13},"end":{"line":454,"column":69}},"type":"cond-expr","locations":[{"start":{"line":454,"column":43},"end":{"line":454,"column":56}},{"start":{"line":454,"column":59},"end":{"line":454,"column":69}}],"line":454}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"8670ca9eb40ca4c03416a6ea1ec9275ad29f1219"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Dashboard.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Dashboard.js","statementMap":{"0":{"start":{"line":15,"column":18},"end":{"line":281,"column":1}},"1":{"start":{"line":16,"column":19},"end":{"line":16,"column":28}},"2":{"start":{"line":18,"column":37},"end":{"line":32,"column":4}},"3":{"start":{"line":19,"column":68},"end":{"line":24,"column":6}},"4":{"start":{"line":26,"column":4},"end":{"line":31,"column":6}},"5":{"start":{"line":34,"column":31},"end":{"line":37,"column":4}},"6":{"start":{"line":35,"column":21},"end":{"line":35,"column":57}},"7":{"start":{"line":36,"column":4},"end":{"line":36,"column":30}},"8":{"start":{"line":39,"column":39},"end":{"line":45,"column":4}},"9":{"start":{"line":40,"column":4},"end":{"line":43,"column":5}},"10":{"start":{"line":41,"column":23},"end":{"line":41,"column":67}},"11":{"start":{"line":42,"column":6},"end":{"line":42,"column":40}},"12":{"start":{"line":44,"column":4},"end":{"line":44,"column":14}},"13":{"start":{"line":47,"column":21},"end":{"line":72,"column":3}},"14":{"start":{"line":74,"column":22},"end":{"line":79,"column":3}},"15":{"start":{"line":75,"column":17},"end":{"line":75,"column":38}},"16":{"start":{"line":76,"column":4},"end":{"line":76,"column":41}},"17":{"start":{"line":76,"column":19},"end":{"line":76,"column":41}},"18":{"start":{"line":77,"column":4},"end":{"line":77,"column":43}},"19":{"start":{"line":77,"column":19},"end":{"line":77,"column":43}},"20":{"start":{"line":78,"column":4},"end":{"line":78,"column":26}},"21":{"start":{"line":81,"column":2},"end":{"line":91,"column":3}},"22":{"start":{"line":82,"column":4},"end":{"line":90,"column":6}},"23":{"start":{"line":93,"column":2},"end":{"line":280,"column":4}},"24":{"start":{"line":107,"column":10},"end":{"line":127,"column":16}},"25":{"start":{"line":142,"column":20},"end":{"line":155,"column":26}},"26":{"start":{"line":175,"column":20},"end":{"line":195,"column":26}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":15,"column":18},"end":{"line":15,"column":19}},"loc":{"start":{"line":15,"column":24},"end":{"line":281,"column":1}},"line":15},"1":{"name":"(anonymous_1)","decl":{"start":{"line":18,"column":65},"end":{"line":18,"column":66}},"loc":{"start":{"line":18,"column":77},"end":{"line":32,"column":3}},"line":18},"2":{"name":"(anonymous_2)","decl":{"start":{"line":34,"column":55},"end":{"line":34,"column":56}},"loc":{"start":{"line":34,"column":67},"end":{"line":37,"column":3}},"line":34},"3":{"name":"(anonymous_3)","decl":{"start":{"line":39,"column":71},"end":{"line":39,"column":72}},"loc":{"start":{"line":39,"column":83},"end":{"line":45,"column":3}},"line":39},"4":{"name":"(anonymous_4)","decl":{"start":{"line":74,"column":22},"end":{"line":74,"column":23}},"loc":{"start":{"line":74,"column":28},"end":{"line":79,"column":3}},"line":74},"5":{"name":"(anonymous_5)","decl":{"start":{"line":106,"column":24},"end":{"line":106,"column":25}},"loc":{"start":{"line":107,"column":10},"end":{"line":127,"column":16}},"line":107},"6":{"name":"(anonymous_6)","decl":{"start":{"line":141,"column":34},"end":{"line":141,"column":35}},"loc":{"start":{"line":142,"column":20},"end":{"line":155,"column":26}},"line":142},"7":{"name":"(anonymous_7)","decl":{"start":{"line":174,"column":42},"end":{"line":174,"column":43}},"loc":{"start":{"line":175,"column":20},"end":{"line":195,"column":26}},"line":175}},"branchMap":{"0":{"loc":{"start":{"line":22,"column":6},"end":{"line":22,"column":149}},"type":"cond-expr","locations":[{"start":{"line":22,"column":35},"end":{"line":22,"column":108}},{"start":{"line":22,"column":111},"end":{"line":22,"column":149}}],"line":22},"1":{"loc":{"start":{"line":23,"column":6},"end":{"line":23,"column":157}},"type":"cond-expr","locations":[{"start":{"line":23,"column":90},"end":{"line":23,"column":125}},{"start":{"line":23,"column":128},"end":{"line":23,"column":157}}],"line":23},"2":{"loc":{"start":{"line":23,"column":6},"end":{"line":23,"column":87}},"type":"binary-expr","locations":[{"start":{"line":23,"column":6},"end":{"line":23,"column":31}},{"start":{"line":23,"column":35},"end":{"line":23,"column":57}},{"start":{"line":23,"column":61},"end":{"line":23,"column":87}}],"line":23},"3":{"loc":{"start":{"line":27,"column":17},"end":{"line":27,"column":52}},"type":"binary-expr","locations":[{"start":{"line":27,"column":17},"end":{"line":27,"column":47}},{"start":{"line":27,"column":51},"end":{"line":27,"column":52}}],"line":27},"4":{"loc":{"start":{"line":28,"column":25},"end":{"line":28,"column":68}},"type":"binary-expr","locations":[{"start":{"line":28,"column":25},"end":{"line":28,"column":63}},{"start":{"line":28,"column":67},"end":{"line":28,"column":68}}],"line":28},"5":{"loc":{"start":{"line":29,"column":23},"end":{"line":29,"column":64}},"type":"binary-expr","locations":[{"start":{"line":29,"column":23},"end":{"line":29,"column":59}},{"start":{"line":29,"column":63},"end":{"line":29,"column":64}}],"line":29},"6":{"loc":{"start":{"line":30,"column":22},"end":{"line":30,"column":51}},"type":"binary-expr","locations":[{"start":{"line":30,"column":22},"end":{"line":30,"column":46}},{"start":{"line":30,"column":50},"end":{"line":30,"column":51}}],"line":30},"7":{"loc":{"start":{"line":40,"column":4},"end":{"line":43,"column":5}},"type":"if","locations":[{"start":{"line":40,"column":4},"end":{"line":43,"column":5}},{"start":{},"end":{}}],"line":40},"8":{"loc":{"start":{"line":50,"column":13},"end":{"line":50,"column":34}},"type":"binary-expr","locations":[{"start":{"line":50,"column":13},"end":{"line":50,"column":29}},{"start":{"line":50,"column":33},"end":{"line":50,"column":34}}],"line":50},"9":{"loc":{"start":{"line":56,"column":13},"end":{"line":56,"column":42}},"type":"binary-expr","locations":[{"start":{"line":56,"column":13},"end":{"line":56,"column":37}},{"start":{"line":56,"column":41},"end":{"line":56,"column":42}}],"line":56},"10":{"loc":{"start":{"line":62,"column":13},"end":{"line":62,"column":40}},"type":"binary-expr","locations":[{"start":{"line":62,"column":13},"end":{"line":62,"column":35}},{"start":{"line":62,"column":39},"end":{"line":62,"column":40}}],"line":62},"11":{"loc":{"start":{"line":68,"column":13},"end":{"line":68,"column":39}},"type":"binary-expr","locations":[{"start":{"line":68,"column":13},"end":{"line":68,"column":34}},{"start":{"line":68,"column":38},"end":{"line":68,"column":39}}],"line":68},"12":{"loc":{"start":{"line":76,"column":4},"end":{"line":76,"column":41}},"type":"if","locations":[{"start":{"line":76,"column":4},"end":{"line":76,"column":41}},{"start":{},"end":{}}],"line":76},"13":{"loc":{"start":{"line":77,"column":4},"end":{"line":77,"column":43}},"type":"if","locations":[{"start":{"line":77,"column":4},"end":{"line":77,"column":43}},{"start":{},"end":{}}],"line":77},"14":{"loc":{"start":{"line":81,"column":2},"end":{"line":91,"column":3}},"type":"if","locations":[{"start":{"line":81,"column":2},"end":{"line":91,"column":3}},{"start":{},"end":{}}],"line":81},"15":{"loc":{"start":{"line":139,"column":15},"end":{"line":160,"column":15}},"type":"cond-expr","locations":[{"start":{"line":140,"column":16},"end":{"line":157,"column":22}},{"start":{"line":159,"column":16},"end":{"line":159,"column":77}}],"line":139},"16":{"loc":{"start":{"line":172,"column":15},"end":{"line":200,"column":15}},"type":"cond-expr","locations":[{"start":{"line":173,"column":16},"end":{"line":197,"column":22}},{"start":{"line":199,"column":16},"end":{"line":199,"column":85}}],"line":172},"17":{"loc":{"start":{"line":186,"column":26},"end":{"line":190,"column":53}},"type":"cond-expr","locations":[{"start":{"line":186,"column":61},"end":{"line":186,"column":88}},{"start":{"line":187,"column":26},"end":{"line":190,"column":53}}],"line":186},"18":{"loc":{"start":{"line":187,"column":26},"end":{"line":190,"column":53}},"type":"cond-expr","locations":[{"start":{"line":187,"column":62},"end":{"line":187,"column":93}},{"start":{"line":188,"column":26},"end":{"line":190,"column":53}}],"line":187},"19":{"loc":{"start":{"line":188,"column":26},"end":{"line":190,"column":53}},"type":"cond-expr","locations":[{"start":{"line":188,"column":65},"end":{"line":188,"column":94}},{"start":{"line":189,"column":26},"end":{"line":190,"column":53}}],"line":188},"20":{"loc":{"start":{"line":189,"column":26},"end":{"line":190,"column":53}},"type":"cond-expr","locations":[{"start":{"line":189,"column":62},"end":{"line":189,"column":87}},{"start":{"line":190,"column":26},"end":{"line":190,"column":53}}],"line":189},"21":{"loc":{"start":{"line":213,"column":13},"end":{"line":233,"column":13}},"type":"binary-expr","locations":[{"start":{"line":213,"column":13},"end":{"line":213,"column":38}},{"start":{"line":214,"column":14},"end":{"line":232,"column":21}}],"line":213},"22":{"loc":{"start":{"line":235,"column":13},"end":{"line":255,"column":13}},"type":"binary-expr","locations":[{"start":{"line":235,"column":13},"end":{"line":235,"column":39}},{"start":{"line":236,"column":14},"end":{"line":254,"column":18}}],"line":235}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0,0],"14":[0,0],"15":[0,0],"16":[0,0],"17":[0,0],"18":[0,0],"19":[0,0],"20":[0,0],"21":[0,0],"22":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"93b46c27ec5d549e65ff63ce546c311864d88172"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/EmployerDetails.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/EmployerDetails.js","statementMap":{"0":{"start":{"line":7,"column":24},"end":{"line":109,"column":1}},"1":{"start":{"line":8,"column":17},"end":{"line":8,"column":28}},"2":{"start":{"line":10,"column":40},"end":{"line":13,"column":4}},"3":{"start":{"line":11,"column":21},"end":{"line":11,"column":60}},"4":{"start":{"line":12,"column":4},"end":{"line":12,"column":25}},"5":{"start":{"line":15,"column":2},"end":{"line":21,"column":3}},"6":{"start":{"line":16,"column":4},"end":{"line":20,"column":6}},"7":{"start":{"line":23,"column":2},"end":{"line":32,"column":3}},"8":{"start":{"line":24,"column":4},"end":{"line":31,"column":6}},"9":{"start":{"line":34,"column":2},"end":{"line":108,"column":4}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":7,"column":24},"end":{"line":7,"column":25}},"loc":{"start":{"line":7,"column":30},"end":{"line":109,"column":1}},"line":7},"1":{"name":"(anonymous_1)","decl":{"start":{"line":10,"column":67},"end":{"line":10,"column":68}},"loc":{"start":{"line":10,"column":79},"end":{"line":13,"column":3}},"line":10}},"branchMap":{"0":{"loc":{"start":{"line":15,"column":2},"end":{"line":21,"column":3}},"type":"if","locations":[{"start":{"line":15,"column":2},"end":{"line":21,"column":3}},{"start":{},"end":{}}],"line":15},"1":{"loc":{"start":{"line":23,"column":2},"end":{"line":32,"column":3}},"type":"if","locations":[{"start":{"line":23,"column":2},"end":{"line":32,"column":3}},{"start":{},"end":{}}],"line":23},"2":{"loc":{"start":{"line":52,"column":17},"end":{"line":57,"column":17}},"type":"binary-expr","locations":[{"start":{"line":52,"column":17},"end":{"line":52,"column":34}},{"start":{"line":53,"column":18},"end":{"line":56,"column":24}}],"line":52},"3":{"loc":{"start":{"line":59,"column":17},"end":{"line":64,"column":17}},"type":"binary-expr","locations":[{"start":{"line":59,"column":17},"end":{"line":59,"column":38}},{"start":{"line":60,"column":18},"end":{"line":63,"column":24}}],"line":59},"4":{"loc":{"start":{"line":66,"column":17},"end":{"line":73,"column":17}},"type":"binary-expr","locations":[{"start":{"line":66,"column":17},"end":{"line":66,"column":33}},{"start":{"line":67,"column":18},"end":{"line":72,"column":24}}],"line":66},"5":{"loc":{"start":{"line":75,"column":17},"end":{"line":80,"column":17}},"type":"binary-expr","locations":[{"start":{"line":75,"column":17},"end":{"line":75,"column":31}},{"start":{"line":76,"column":18},"end":{"line":79,"column":24}}],"line":75},"6":{"loc":{"start":{"line":87,"column":7},"end":{"line":94,"column":7}},"type":"binary-expr","locations":[{"start":{"line":87,"column":7},"end":{"line":87,"column":27}},{"start":{"line":88,"column":8},"end":{"line":93,"column":14}}],"line":87},"7":{"loc":{"start":{"line":96,"column":7},"end":{"line":106,"column":7}},"type":"binary-expr","locations":[{"start":{"line":96,"column":7},"end":{"line":96,"column":23}},{"start":{"line":97,"column":8},"end":{"line":105,"column":14}}],"line":96}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0},"f":{"0":0,"1":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"32b706f015750118857828bf4682e1426b1771b0"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Employers.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Employers.js","statementMap":{"0":{"start":{"line":7,"column":18},"end":{"line":96,"column":1}},"1":{"start":{"line":8,"column":30},"end":{"line":11,"column":4}},"2":{"start":{"line":9,"column":21},"end":{"line":9,"column":54}},"3":{"start":{"line":10,"column":4},"end":{"line":10,"column":25}},"4":{"start":{"line":13,"column":2},"end":{"line":19,"column":3}},"5":{"start":{"line":14,"column":4},"end":{"line":18,"column":6}},"6":{"start":{"line":21,"column":2},"end":{"line":95,"column":4}},"7":{"start":{"line":33,"column":12},"end":{"line":82,"column":18}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":7,"column":18},"end":{"line":7,"column":19}},"loc":{"start":{"line":7,"column":24},"end":{"line":96,"column":1}},"line":7},"1":{"name":"(anonymous_1)","decl":{"start":{"line":8,"column":52},"end":{"line":8,"column":53}},"loc":{"start":{"line":8,"column":64},"end":{"line":11,"column":3}},"line":8},"2":{"name":"(anonymous_2)","decl":{"start":{"line":32,"column":19},"end":{"line":32,"column":20}},"loc":{"start":{"line":33,"column":12},"end":{"line":82,"column":18}},"line":33}},"branchMap":{"0":{"loc":{"start":{"line":13,"column":2},"end":{"line":19,"column":3}},"type":"if","locations":[{"start":{"line":13,"column":2},"end":{"line":19,"column":3}},{"start":{},"end":{}}],"line":13},"1":{"loc":{"start":{"line":31,"column":9},"end":{"line":92,"column":9}},"type":"cond-expr","locations":[{"start":{"line":32,"column":10},"end":{"line":83,"column":12}},{"start":{"line":85,"column":10},"end":{"line":91,"column":16}}],"line":31},"2":{"loc":{"start":{"line":49,"column":21},"end":{"line":54,"column":21}},"type":"binary-expr","locations":[{"start":{"line":49,"column":21},"end":{"line":49,"column":38}},{"start":{"line":50,"column":22},"end":{"line":53,"column":28}}],"line":49},"3":{"loc":{"start":{"line":56,"column":21},"end":{"line":61,"column":21}},"type":"binary-expr","locations":[{"start":{"line":56,"column":21},"end":{"line":56,"column":42}},{"start":{"line":57,"column":22},"end":{"line":60,"column":28}}],"line":56},"4":{"loc":{"start":{"line":63,"column":21},"end":{"line":70,"column":21}},"type":"binary-expr","locations":[{"start":{"line":63,"column":21},"end":{"line":63,"column":37}},{"start":{"line":64,"column":22},"end":{"line":69,"column":28}}],"line":63},"5":{"loc":{"start":{"line":72,"column":21},"end":{"line":78,"column":21}},"type":"binary-expr","locations":[{"start":{"line":72,"column":21},"end":{"line":72,"column":41}},{"start":{"line":73,"column":22},"end":{"line":77,"column":28}}],"line":72}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0},"f":{"0":0,"1":0,"2":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"6ae73b243b91efa5285da50721e971eeebd7c9a9"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/JobDetails.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/JobDetails.js","statementMap":{"0":{"start":{"line":9,"column":19},"end":{"line":291,"column":1}},"1":{"start":{"line":10,"column":17},"end":{"line":10,"column":28}},"2":{"start":{"line":11,"column":19},"end":{"line":11,"column":28}},"3":{"start":{"line":12,"column":34},"end":{"line":12,"column":49}},"4":{"start":{"line":13,"column":40},"end":{"line":13,"column":52}},"5":{"start":{"line":15,"column":35},"end":{"line":18,"column":4}},"6":{"start":{"line":16,"column":21},"end":{"line":16,"column":55}},"7":{"start":{"line":17,"column":4},"end":{"line":17,"column":25}},"8":{"start":{"line":20,"column":22},"end":{"line":39,"column":3}},"9":{"start":{"line":21,"column":4},"end":{"line":24,"column":5}},"10":{"start":{"line":22,"column":6},"end":{"line":22,"column":51}},"11":{"start":{"line":23,"column":6},"end":{"line":23,"column":13}},"12":{"start":{"line":26,"column":4},"end":{"line":26,"column":22}},"13":{"start":{"line":27,"column":4},"end":{"line":38,"column":5}},"14":{"start":{"line":28,"column":6},"end":{"line":31,"column":9}},"15":{"start":{"line":32,"column":6},"end":{"line":32,"column":59}},"16":{"start":{"line":33,"column":6},"end":{"line":33,"column":25}},"17":{"start":{"line":35,"column":6},"end":{"line":35,"column":81}},"18":{"start":{"line":37,"column":6},"end":{"line":37,"column":25}},"19":{"start":{"line":41,"column":2},"end":{"line":47,"column":3}},"20":{"start":{"line":42,"column":4},"end":{"line":46,"column":6}},"21":{"start":{"line":49,"column":2},"end":{"line":63,"column":3}},"22":{"start":{"line":50,"column":4},"end":{"line":62,"column":6}},"23":{"start":{"line":65,"column":23},"end":{"line":70,"column":3}},"24":{"start":{"line":66,"column":4},"end":{"line":66,"column":52}},"25":{"start":{"line":66,"column":22},"end":{"line":66,"column":52}},"26":{"start":{"line":67,"column":4},"end":{"line":67,"column":66}},"27":{"start":{"line":67,"column":14},"end":{"line":67,"column":66}},"28":{"start":{"line":68,"column":4},"end":{"line":68,"column":65}},"29":{"start":{"line":68,"column":14},"end":{"line":68,"column":65}},"30":{"start":{"line":69,"column":4},"end":{"line":69,"column":77}},"31":{"start":{"line":72,"column":2},"end":{"line":290,"column":4}},"32":{"start":{"line":117,"column":20},"end":{"line":117,"column":80}},"33":{"start":{"line":131,"column":20},"end":{"line":131,"column":83}},"34":{"start":{"line":145,"column":20},"end":{"line":150,"column":27}},"35":{"start":{"line":164,"column":20},"end":{"line":164,"column":76}},"36":{"start":{"line":273,"column":39},"end":{"line":273,"column":69}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":9,"column":19},"end":{"line":9,"column":20}},"loc":{"start":{"line":9,"column":25},"end":{"line":291,"column":1}},"line":9},"1":{"name":"(anonymous_1)","decl":{"start":{"line":15,"column":57},"end":{"line":15,"column":58}},"loc":{"start":{"line":15,"column":69},"end":{"line":18,"column":3}},"line":15},"2":{"name":"(anonymous_2)","decl":{"start":{"line":20,"column":22},"end":{"line":20,"column":23}},"loc":{"start":{"line":20,"column":34},"end":{"line":39,"column":3}},"line":20},"3":{"name":"(anonymous_3)","decl":{"start":{"line":65,"column":23},"end":{"line":65,"column":24}},"loc":{"start":{"line":65,"column":55},"end":{"line":70,"column":3}},"line":65},"4":{"name":"(anonymous_4)","decl":{"start":{"line":116,"column":40},"end":{"line":116,"column":41}},"loc":{"start":{"line":117,"column":20},"end":{"line":117,"column":80}},"line":117},"5":{"name":"(anonymous_5)","decl":{"start":{"line":130,"column":44},"end":{"line":130,"column":45}},"loc":{"start":{"line":131,"column":20},"end":{"line":131,"column":83}},"line":131},"6":{"name":"(anonymous_6)","decl":{"start":{"line":144,"column":43},"end":{"line":144,"column":44}},"loc":{"start":{"line":145,"column":20},"end":{"line":150,"column":27}},"line":145},"7":{"name":"(anonymous_7)","decl":{"start":{"line":163,"column":36},"end":{"line":163,"column":37}},"loc":{"start":{"line":164,"column":20},"end":{"line":164,"column":76}},"line":164},"8":{"name":"(anonymous_8)","decl":{"start":{"line":273,"column":32},"end":{"line":273,"column":33}},"loc":{"start":{"line":273,"column":39},"end":{"line":273,"column":69}},"line":273}},"branchMap":{"0":{"loc":{"start":{"line":21,"column":4},"end":{"line":24,"column":5}},"type":"if","locations":[{"start":{"line":21,"column":4},"end":{"line":24,"column":5}},{"start":{},"end":{}}],"line":21},"1":{"loc":{"start":{"line":35,"column":18},"end":{"line":35,"column":79}},"type":"binary-expr","locations":[{"start":{"line":35,"column":18},"end":{"line":35,"column":45}},{"start":{"line":35,"column":49},"end":{"line":35,"column":79}}],"line":35},"2":{"loc":{"start":{"line":41,"column":2},"end":{"line":47,"column":3}},"type":"if","locations":[{"start":{"line":41,"column":2},"end":{"line":47,"column":3}},{"start":{},"end":{}}],"line":41},"3":{"loc":{"start":{"line":49,"column":2},"end":{"line":63,"column":3}},"type":"if","locations":[{"start":{"line":49,"column":2},"end":{"line":63,"column":3}},{"start":{},"end":{}}],"line":49},"4":{"loc":{"start":{"line":65,"column":34},"end":{"line":65,"column":50}},"type":"default-arg","locations":[{"start":{"line":65,"column":45},"end":{"line":65,"column":50}}],"line":65},"5":{"loc":{"start":{"line":66,"column":4},"end":{"line":66,"column":52}},"type":"if","locations":[{"start":{"line":66,"column":4},"end":{"line":66,"column":52}},{"start":{},"end":{}}],"line":66},"6":{"loc":{"start":{"line":66,"column":8},"end":{"line":66,"column":20}},"type":"binary-expr","locations":[{"start":{"line":66,"column":8},"end":{"line":66,"column":12}},{"start":{"line":66,"column":16},"end":{"line":66,"column":20}}],"line":66},"7":{"loc":{"start":{"line":67,"column":4},"end":{"line":67,"column":66}},"type":"if","locations":[{"start":{"line":67,"column":4},"end":{"line":67,"column":66}},{"start":{},"end":{}}],"line":67},"8":{"loc":{"start":{"line":68,"column":4},"end":{"line":68,"column":65}},"type":"if","locations":[{"start":{"line":68,"column":4},"end":{"line":68,"column":65}},{"start":{},"end":{}}],"line":68},"9":{"loc":{"start":{"line":85,"column":9},"end":{"line":95,"column":9}},"type":"binary-expr","locations":[{"start":{"line":85,"column":9},"end":{"line":85,"column":35}},{"start":{"line":85,"column":39},"end":{"line":85,"column":62}},{"start":{"line":86,"column":10},"end":{"line":94,"column":16}}],"line":85},"10":{"loc":{"start":{"line":92,"column":15},"end":{"line":92,"column":53}},"type":"cond-expr","locations":[{"start":{"line":92,"column":26},"end":{"line":92,"column":39}},{"start":{"line":92,"column":42},"end":{"line":92,"column":53}}],"line":92},"11":{"loc":{"start":{"line":111,"column":11},"end":{"line":122,"column":11}},"type":"binary-expr","locations":[{"start":{"line":111,"column":11},"end":{"line":111,"column":27}},{"start":{"line":111,"column":31},"end":{"line":111,"column":58}},{"start":{"line":112,"column":12},"end":{"line":121,"column":18}}],"line":111},"12":{"loc":{"start":{"line":125,"column":11},"end":{"line":136,"column":11}},"type":"binary-expr","locations":[{"start":{"line":125,"column":11},"end":{"line":125,"column":31}},{"start":{"line":125,"column":35},"end":{"line":125,"column":66}},{"start":{"line":126,"column":12},"end":{"line":135,"column":18}}],"line":125},"13":{"loc":{"start":{"line":139,"column":11},"end":{"line":155,"column":11}},"type":"binary-expr","locations":[{"start":{"line":139,"column":11},"end":{"line":139,"column":30}},{"start":{"line":139,"column":34},"end":{"line":139,"column":64}},{"start":{"line":140,"column":12},"end":{"line":154,"column":18}}],"line":139},"14":{"loc":{"start":{"line":158,"column":11},"end":{"line":169,"column":11}},"type":"binary-expr","locations":[{"start":{"line":158,"column":11},"end":{"line":158,"column":23}},{"start":{"line":158,"column":27},"end":{"line":158,"column":50}},{"start":{"line":159,"column":12},"end":{"line":168,"column":18}}],"line":158},"15":{"loc":{"start":{"line":183,"column":21},"end":{"line":185,"column":21}},"type":"binary-expr","locations":[{"start":{"line":183,"column":21},"end":{"line":183,"column":39}},{"start":{"line":184,"column":22},"end":{"line":184,"column":82}}],"line":183},"16":{"loc":{"start":{"line":207,"column":17},"end":{"line":216,"column":17}},"type":"binary-expr","locations":[{"start":{"line":207,"column":17},"end":{"line":207,"column":37}},{"start":{"line":208,"column":18},"end":{"line":215,"column":24}}],"line":207},"17":{"loc":{"start":{"line":227,"column":17},"end":{"line":236,"column":17}},"type":"binary-expr","locations":[{"start":{"line":227,"column":17},"end":{"line":227,"column":41}},{"start":{"line":228,"column":18},"end":{"line":235,"column":24}}],"line":227},"18":{"loc":{"start":{"line":247,"column":17},"end":{"line":249,"column":17}},"type":"binary-expr","locations":[{"start":{"line":247,"column":17},"end":{"line":247,"column":29}},{"start":{"line":248,"column":18},"end":{"line":248,"column":73}}],"line":247},"19":{"loc":{"start":{"line":250,"column":17},"end":{"line":252,"column":17}},"type":"binary-expr","locations":[{"start":{"line":250,"column":17},"end":{"line":250,"column":33}},{"start":{"line":251,"column":18},"end":{"line":251,"column":87}}],"line":250},"20":{"loc":{"start":{"line":258,"column":11},"end":{"line":286,"column":11}},"type":"binary-expr","locations":[{"start":{"line":258,"column":11},"end":{"line":258,"column":37}},{"start":{"line":258,"column":41},"end":{"line":258,"column":64}},{"start":{"line":259,"column":12},"end":{"line":285,"column":18}}],"line":258},"21":{"loc":{"start":{"line":278,"column":30},"end":{"line":278,"column":61}},"type":"binary-expr","locations":[{"start":{"line":278,"column":30},"end":{"line":278,"column":38}},{"start":{"line":278,"column":42},"end":{"line":278,"column":61}}],"line":278},"22":{"loc":{"start":{"line":281,"column":21},"end":{"line":281,"column":68}},"type":"cond-expr","locations":[{"start":{"line":281,"column":32},"end":{"line":281,"column":45}},{"start":{"line":281,"column":48},"end":{"line":281,"column":68}}],"line":281}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0,0],"10":[0,0],"11":[0,0,0],"12":[0,0,0],"13":[0,0,0],"14":[0,0,0],"15":[0,0],"16":[0,0],"17":[0,0],"18":[0,0],"19":[0,0],"20":[0,0,0],"21":[0,0],"22":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"478f5b3321caf7230bae18b5606c730e6441c612"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Jobs.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Jobs.js","statementMap":{"0":{"start":{"line":8,"column":13},"end":{"line":315,"column":1}},"1":{"start":{"line":9,"column":19},"end":{"line":9,"column":28}},"2":{"start":{"line":10,"column":32},"end":{"line":15,"column":4}},"3":{"start":{"line":23,"column":6},"end":{"line":31,"column":4}},"4":{"start":{"line":24,"column":19},"end":{"line":24,"column":40}},"5":{"start":{"line":25,"column":4},"end":{"line":27,"column":7}},"6":{"start":{"line":26,"column":6},"end":{"line":26,"column":43}},"7":{"start":{"line":26,"column":17},"end":{"line":26,"column":43}},"8":{"start":{"line":29,"column":21},"end":{"line":29,"column":70}},"9":{"start":{"line":30,"column":4},"end":{"line":30,"column":25}},"10":{"start":{"line":33,"column":29},"end":{"line":38,"column":3}},"11":{"start":{"line":34,"column":4},"end":{"line":37,"column":7}},"12":{"start":{"line":40,"column":23},"end":{"line":43,"column":3}},"13":{"start":{"line":41,"column":4},"end":{"line":41,"column":23}},"14":{"start":{"line":42,"column":4},"end":{"line":42,"column":14}},"15":{"start":{"line":45,"column":23},"end":{"line":50,"column":3}},"16":{"start":{"line":46,"column":4},"end":{"line":46,"column":52}},"17":{"start":{"line":46,"column":22},"end":{"line":46,"column":52}},"18":{"start":{"line":47,"column":4},"end":{"line":47,"column":66}},"19":{"start":{"line":47,"column":14},"end":{"line":47,"column":66}},"20":{"start":{"line":48,"column":4},"end":{"line":48,"column":65}},"21":{"start":{"line":48,"column":14},"end":{"line":48,"column":65}},"22":{"start":{"line":49,"column":4},"end":{"line":49,"column":77}},"23":{"start":{"line":52,"column":25},"end":{"line":59,"column":3}},"24":{"start":{"line":53,"column":4},"end":{"line":58,"column":5}},"25":{"start":{"line":54,"column":21},"end":{"line":54,"column":58}},"26":{"start":{"line":55,"column":21},"end":{"line":55,"column":60}},"27":{"start":{"line":56,"column":21},"end":{"line":56,"column":54}},"28":{"start":{"line":57,"column":15},"end":{"line":57,"column":50}},"29":{"start":{"line":61,"column":2},"end":{"line":71,"column":3}},"30":{"start":{"line":62,"column":4},"end":{"line":70,"column":6}},"31":{"start":{"line":73,"column":2},"end":{"line":87,"column":3}},"32":{"start":{"line":74,"column":4},"end":{"line":86,"column":6}},"33":{"start":{"line":80,"column":25},"end":{"line":80,"column":34}},"34":{"start":{"line":89,"column":2},"end":{"line":314,"column":4}},"35":{"start":{"line":210,"column":12},"end":{"line":270,"column":18}},"36":{"start":{"line":252,"column":28},"end":{"line":257,"column":35}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":8,"column":13},"end":{"line":8,"column":14}},"loc":{"start":{"line":8,"column":19},"end":{"line":315,"column":1}},"line":8},"1":{"name":"(anonymous_1)","decl":{"start":{"line":23,"column":34},"end":{"line":23,"column":35}},"loc":{"start":{"line":23,"column":46},"end":{"line":31,"column":3}},"line":23},"2":{"name":"(anonymous_2)","decl":{"start":{"line":25,"column":36},"end":{"line":25,"column":37}},"loc":{"start":{"line":25,"column":54},"end":{"line":27,"column":5}},"line":25},"3":{"name":"(anonymous_3)","decl":{"start":{"line":33,"column":29},"end":{"line":33,"column":30}},"loc":{"start":{"line":33,"column":36},"end":{"line":38,"column":3}},"line":33},"4":{"name":"(anonymous_4)","decl":{"start":{"line":40,"column":23},"end":{"line":40,"column":24}},"loc":{"start":{"line":40,"column":30},"end":{"line":43,"column":3}},"line":40},"5":{"name":"(anonymous_5)","decl":{"start":{"line":45,"column":23},"end":{"line":45,"column":24}},"loc":{"start":{"line":45,"column":55},"end":{"line":50,"column":3}},"line":45},"6":{"name":"(anonymous_6)","decl":{"start":{"line":52,"column":25},"end":{"line":52,"column":26}},"loc":{"start":{"line":52,"column":37},"end":{"line":59,"column":3}},"line":52},"7":{"name":"(anonymous_7)","decl":{"start":{"line":80,"column":19},"end":{"line":80,"column":20}},"loc":{"start":{"line":80,"column":25},"end":{"line":80,"column":34}},"line":80},"8":{"name":"(anonymous_8)","decl":{"start":{"line":209,"column":24},"end":{"line":209,"column":25}},"loc":{"start":{"line":210,"column":12},"end":{"line":270,"column":18}},"line":210},"9":{"name":"(anonymous_9)","decl":{"start":{"line":251,"column":63},"end":{"line":251,"column":64}},"loc":{"start":{"line":252,"column":28},"end":{"line":257,"column":35}},"line":252}},"branchMap":{"0":{"loc":{"start":{"line":26,"column":6},"end":{"line":26,"column":43}},"type":"if","locations":[{"start":{"line":26,"column":6},"end":{"line":26,"column":43}},{"start":{},"end":{}}],"line":26},"1":{"loc":{"start":{"line":45,"column":34},"end":{"line":45,"column":50}},"type":"default-arg","locations":[{"start":{"line":45,"column":45},"end":{"line":45,"column":50}}],"line":45},"2":{"loc":{"start":{"line":46,"column":4},"end":{"line":46,"column":52}},"type":"if","locations":[{"start":{"line":46,"column":4},"end":{"line":46,"column":52}},{"start":{},"end":{}}],"line":46},"3":{"loc":{"start":{"line":46,"column":8},"end":{"line":46,"column":20}},"type":"binary-expr","locations":[{"start":{"line":46,"column":8},"end":{"line":46,"column":12}},{"start":{"line":46,"column":16},"end":{"line":46,"column":20}}],"line":46},"4":{"loc":{"start":{"line":47,"column":4},"end":{"line":47,"column":66}},"type":"if","locations":[{"start":{"line":47,"column":4},"end":{"line":47,"column":66}},{"start":{},"end":{}}],"line":47},"5":{"loc":{"start":{"line":48,"column":4},"end":{"line":48,"column":65}},"type":"if","locations":[{"start":{"line":48,"column":4},"end":{"line":48,"column":65}},{"start":{},"end":{}}],"line":48},"6":{"loc":{"start":{"line":53,"column":4},"end":{"line":58,"column":5}},"type":"switch","locations":[{"start":{"line":54,"column":6},"end":{"line":54,"column":58}},{"start":{"line":55,"column":6},"end":{"line":55,"column":60}},{"start":{"line":56,"column":6},"end":{"line":56,"column":54}},{"start":{"line":57,"column":6},"end":{"line":57,"column":50}}],"line":53},"7":{"loc":{"start":{"line":61,"column":2},"end":{"line":71,"column":3}},"type":"if","locations":[{"start":{"line":61,"column":2},"end":{"line":71,"column":3}},{"start":{},"end":{}}],"line":61},"8":{"loc":{"start":{"line":73,"column":2},"end":{"line":87,"column":3}},"type":"if","locations":[{"start":{"line":73,"column":2},"end":{"line":87,"column":3}},{"start":{},"end":{}}],"line":73},"9":{"loc":{"start":{"line":77,"column":46},"end":{"line":77,"column":89}},"type":"binary-expr","locations":[{"start":{"line":77,"column":46},"end":{"line":77,"column":60}},{"start":{"line":77,"column":64},"end":{"line":77,"column":89}}],"line":77},"10":{"loc":{"start":{"line":98,"column":9},"end":{"line":106,"column":9}},"type":"binary-expr","locations":[{"start":{"line":98,"column":10},"end":{"line":98,"column":35}},{"start":{"line":98,"column":39},"end":{"line":98,"column":65}},{"start":{"line":99,"column":10},"end":{"line":105,"column":17}}],"line":98},"11":{"loc":{"start":{"line":208,"column":9},"end":{"line":280,"column":9}},"type":"cond-expr","locations":[{"start":{"line":209,"column":10},"end":{"line":271,"column":12}},{"start":{"line":273,"column":10},"end":{"line":279,"column":16}}],"line":208},"12":{"loc":{"start":{"line":232,"column":25},"end":{"line":232,"column":88}},"type":"binary-expr","locations":[{"start":{"line":232,"column":25},"end":{"line":232,"column":43}},{"start":{"line":232,"column":47},"end":{"line":232,"column":88}}],"line":232},"13":{"loc":{"start":{"line":248,"column":21},"end":{"line":266,"column":21}},"type":"binary-expr","locations":[{"start":{"line":248,"column":21},"end":{"line":248,"column":40}},{"start":{"line":248,"column":44},"end":{"line":248,"column":74}},{"start":{"line":249,"column":22},"end":{"line":265,"column":28}}],"line":248},"14":{"loc":{"start":{"line":259,"column":27},"end":{"line":263,"column":27}},"type":"binary-expr","locations":[{"start":{"line":259,"column":27},"end":{"line":259,"column":57}},{"start":{"line":260,"column":28},"end":{"line":262,"column":35}}],"line":259},"15":{"loc":{"start":{"line":284,"column":7},"end":{"line":312,"column":7}},"type":"binary-expr","locations":[{"start":{"line":284,"column":7},"end":{"line":284,"column":23}},{"start":{"line":284,"column":27},"end":{"line":284,"column":52}},{"start":{"line":285,"column":8},"end":{"line":311,"column":14}}],"line":284}},"s":{"0":2,"1":3,"2":3,"3":3,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":3,"11":0,"12":3,"13":0,"14":0,"15":3,"16":1,"17":0,"18":1,"19":0,"20":1,"21":0,"22":1,"23":3,"24":1,"25":1,"26":0,"27":0,"28":0,"29":3,"30":1,"31":2,"32":1,"33":1,"34":1,"35":1,"36":0},"f":{"0":3,"1":0,"2":0,"3":0,"4":0,"5":1,"6":1,"7":1,"8":1,"9":0},"b":{"0":[0,0],"1":[1],"2":[0,1],"3":[1,0],"4":[0,1],"5":[0,1],"6":[1,0,0,0],"7":[1,2],"8":[1,1],"9":[1,0],"10":[1,0,1],"11":[1,0],"12":[1,0],"13":[1,0,0],"14":[0,0],"15":[1,0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"03b2df18168b4235031632891721553129774dd3"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Login.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Login.js","statementMap":{"0":{"start":{"line":6,"column":14},"end":{"line":134,"column":1}},"1":{"start":{"line":7,"column":20},"end":{"line":7,"column":29}},"2":{"start":{"line":8,"column":34},"end":{"line":11,"column":4}},"3":{"start":{"line":12,"column":42},"end":{"line":12,"column":57}},"4":{"start":{"line":13,"column":32},"end":{"line":13,"column":47}},"5":{"start":{"line":15,"column":23},"end":{"line":20,"column":3}},"6":{"start":{"line":16,"column":4},"end":{"line":19,"column":7}},"7":{"start":{"line":22,"column":23},"end":{"line":31,"column":3}},"8":{"start":{"line":23,"column":4},"end":{"line":23,"column":23}},"9":{"start":{"line":24,"column":4},"end":{"line":24,"column":21}},"10":{"start":{"line":26,"column":19},"end":{"line":26,"column":65}},"11":{"start":{"line":28,"column":4},"end":{"line":30,"column":5}},"12":{"start":{"line":29,"column":6},"end":{"line":29,"column":24}},"13":{"start":{"line":33,"column":2},"end":{"line":133,"column":4}},"14":{"start":{"line":96,"column":35},"end":{"line":96,"column":65}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":14},"end":{"line":6,"column":15}},"loc":{"start":{"line":6,"column":20},"end":{"line":134,"column":1}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":15,"column":23},"end":{"line":15,"column":24}},"loc":{"start":{"line":15,"column":30},"end":{"line":20,"column":3}},"line":15},"2":{"name":"(anonymous_2)","decl":{"start":{"line":22,"column":23},"end":{"line":22,"column":24}},"loc":{"start":{"line":22,"column":36},"end":{"line":31,"column":3}},"line":22},"3":{"name":"(anonymous_3)","decl":{"start":{"line":96,"column":29},"end":{"line":96,"column":30}},"loc":{"start":{"line":96,"column":35},"end":{"line":96,"column":65}},"line":96}},"branchMap":{"0":{"loc":{"start":{"line":28,"column":4},"end":{"line":30,"column":5}},"type":"if","locations":[{"start":{"line":28,"column":4},"end":{"line":30,"column":5}},{"start":{},"end":{}}],"line":28},"1":{"loc":{"start":{"line":84,"column":24},"end":{"line":84,"column":58}},"type":"cond-expr","locations":[{"start":{"line":84,"column":39},"end":{"line":84,"column":45}},{"start":{"line":84,"column":48},"end":{"line":84,"column":58}}],"line":84},"2":{"loc":{"start":{"line":98,"column":21},"end":{"line":102,"column":21}},"type":"cond-expr","locations":[{"start":{"line":99,"column":22},"end":{"line":99,"column":52}},{"start":{"line":101,"column":22},"end":{"line":101,"column":49}}],"line":98},"3":{"loc":{"start":{"line":115,"column":15},"end":{"line":115,"column":52}},"type":"cond-expr","locations":[{"start":{"line":115,"column":25},"end":{"line":115,"column":40}},{"start":{"line":115,"column":43},"end":{"line":115,"column":52}}],"line":115}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0},"f":{"0":0,"1":0,"2":0,"3":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"258bc3fd6c11c37e27df268582736b7981db3200"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Profile.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Profile.js","statementMap":{"0":{"start":{"line":6,"column":16},"end":{"line":140,"column":1}},"1":{"start":{"line":7,"column":30},"end":{"line":7,"column":39}},"2":{"start":{"line":8,"column":34},"end":{"line":12,"column":4}},"3":{"start":{"line":13,"column":32},"end":{"line":13,"column":47}},"4":{"start":{"line":15,"column":23},"end":{"line":20,"column":3}},"5":{"start":{"line":16,"column":4},"end":{"line":19,"column":7}},"6":{"start":{"line":22,"column":23},"end":{"line":43,"column":3}},"7":{"start":{"line":23,"column":4},"end":{"line":23,"column":23}},"8":{"start":{"line":24,"column":4},"end":{"line":24,"column":21}},"9":{"start":{"line":26,"column":4},"end":{"line":42,"column":5}},"10":{"start":{"line":27,"column":6},"end":{"line":34,"column":9}},"11":{"start":{"line":36,"column":6},"end":{"line":36,"column":24}},"12":{"start":{"line":37,"column":6},"end":{"line":37,"column":53}},"13":{"start":{"line":39,"column":6},"end":{"line":39,"column":46}},"14":{"start":{"line":41,"column":6},"end":{"line":41,"column":24}},"15":{"start":{"line":45,"column":2},"end":{"line":139,"column":4}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":16},"end":{"line":6,"column":17}},"loc":{"start":{"line":6,"column":22},"end":{"line":140,"column":1}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":15,"column":23},"end":{"line":15,"column":24}},"loc":{"start":{"line":15,"column":30},"end":{"line":20,"column":3}},"line":15},"2":{"name":"(anonymous_2)","decl":{"start":{"line":22,"column":23},"end":{"line":22,"column":24}},"loc":{"start":{"line":22,"column":36},"end":{"line":43,"column":3}},"line":22}},"branchMap":{"0":{"loc":{"start":{"line":9,"column":15},"end":{"line":9,"column":36}},"type":"binary-expr","locations":[{"start":{"line":9,"column":15},"end":{"line":9,"column":30}},{"start":{"line":9,"column":34},"end":{"line":9,"column":36}}],"line":9},"1":{"loc":{"start":{"line":10,"column":14},"end":{"line":10,"column":34}},"type":"binary-expr","locations":[{"start":{"line":10,"column":14},"end":{"line":10,"column":28}},{"start":{"line":10,"column":32},"end":{"line":10,"column":34}}],"line":10},"2":{"loc":{"start":{"line":11,"column":11},"end":{"line":11,"column":28}},"type":"binary-expr","locations":[{"start":{"line":11,"column":11},"end":{"line":11,"column":22}},{"start":{"line":11,"column":26},"end":{"line":11,"column":28}}],"line":11},"3":{"loc":{"start":{"line":132,"column":17},"end":{"line":132,"column":55}},"type":"cond-expr","locations":[{"start":{"line":132,"column":27},"end":{"line":132,"column":38}},{"start":{"line":132,"column":41},"end":{"line":132,"column":55}}],"line":132}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0},"f":{"0":0,"1":0,"2":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"eaa80f3fc0cfc0889fad1d0d8544b75dc176d61b"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Register.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Register.js","statementMap":{"0":{"start":{"line":6,"column":17},"end":{"line":240,"column":1}},"1":{"start":{"line":7,"column":23},"end":{"line":7,"column":32}},"2":{"start":{"line":8,"column":34},"end":{"line":15,"column":4}},"3":{"start":{"line":16,"column":42},"end":{"line":16,"column":57}},"4":{"start":{"line":17,"column":56},"end":{"line":17,"column":71}},"5":{"start":{"line":18,"column":32},"end":{"line":18,"column":47}},"6":{"start":{"line":20,"column":23},"end":{"line":25,"column":3}},"7":{"start":{"line":21,"column":4},"end":{"line":24,"column":7}},"8":{"start":{"line":27,"column":23},"end":{"line":53,"column":3}},"9":{"start":{"line":28,"column":4},"end":{"line":28,"column":23}},"10":{"start":{"line":30,"column":4},"end":{"line":33,"column":5}},"11":{"start":{"line":31,"column":6},"end":{"line":31,"column":38}},"12":{"start":{"line":32,"column":6},"end":{"line":32,"column":13}},"13":{"start":{"line":35,"column":4},"end":{"line":38,"column":5}},"14":{"start":{"line":36,"column":6},"end":{"line":36,"column":59}},"15":{"start":{"line":37,"column":6},"end":{"line":37,"column":13}},"16":{"start":{"line":40,"column":4},"end":{"line":40,"column":21}},"17":{"start":{"line":42,"column":19},"end":{"line":48,"column":6}},"18":{"start":{"line":50,"column":4},"end":{"line":52,"column":5}},"19":{"start":{"line":51,"column":6},"end":{"line":51,"column":24}},"20":{"start":{"line":55,"column":2},"end":{"line":239,"column":4}},"21":{"start":{"line":179,"column":35},"end":{"line":179,"column":65}},"22":{"start":{"line":214,"column":35},"end":{"line":214,"column":79}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":17},"end":{"line":6,"column":18}},"loc":{"start":{"line":6,"column":23},"end":{"line":240,"column":1}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":20,"column":23},"end":{"line":20,"column":24}},"loc":{"start":{"line":20,"column":30},"end":{"line":25,"column":3}},"line":20},"2":{"name":"(anonymous_2)","decl":{"start":{"line":27,"column":23},"end":{"line":27,"column":24}},"loc":{"start":{"line":27,"column":36},"end":{"line":53,"column":3}},"line":27},"3":{"name":"(anonymous_3)","decl":{"start":{"line":179,"column":29},"end":{"line":179,"column":30}},"loc":{"start":{"line":179,"column":35},"end":{"line":179,"column":65}},"line":179},"4":{"name":"(anonymous_4)","decl":{"start":{"line":214,"column":29},"end":{"line":214,"column":30}},"loc":{"start":{"line":214,"column":35},"end":{"line":214,"column":79}},"line":214}},"branchMap":{"0":{"loc":{"start":{"line":30,"column":4},"end":{"line":33,"column":5}},"type":"if","locations":[{"start":{"line":30,"column":4},"end":{"line":33,"column":5}},{"start":{},"end":{}}],"line":30},"1":{"loc":{"start":{"line":35,"column":4},"end":{"line":38,"column":5}},"type":"if","locations":[{"start":{"line":35,"column":4},"end":{"line":38,"column":5}},{"start":{},"end":{}}],"line":35},"2":{"loc":{"start":{"line":50,"column":4},"end":{"line":52,"column":5}},"type":"if","locations":[{"start":{"line":50,"column":4},"end":{"line":52,"column":5}},{"start":{},"end":{}}],"line":50},"3":{"loc":{"start":{"line":167,"column":24},"end":{"line":167,"column":58}},"type":"cond-expr","locations":[{"start":{"line":167,"column":39},"end":{"line":167,"column":45}},{"start":{"line":167,"column":48},"end":{"line":167,"column":58}}],"line":167},"4":{"loc":{"start":{"line":181,"column":21},"end":{"line":185,"column":21}},"type":"cond-expr","locations":[{"start":{"line":182,"column":22},"end":{"line":182,"column":52}},{"start":{"line":184,"column":22},"end":{"line":184,"column":49}}],"line":181},"5":{"loc":{"start":{"line":202,"column":24},"end":{"line":202,"column":65}},"type":"cond-expr","locations":[{"start":{"line":202,"column":46},"end":{"line":202,"column":52}},{"start":{"line":202,"column":55},"end":{"line":202,"column":65}}],"line":202},"6":{"loc":{"start":{"line":216,"column":21},"end":{"line":220,"column":21}},"type":"cond-expr","locations":[{"start":{"line":217,"column":22},"end":{"line":217,"column":52}},{"start":{"line":219,"column":22},"end":{"line":219,"column":49}}],"line":216},"7":{"loc":{"start":{"line":233,"column":15},"end":{"line":233,"column":65}},"type":"cond-expr","locations":[{"start":{"line":233,"column":25},"end":{"line":233,"column":46}},{"start":{"line":233,"column":49},"end":{"line":233,"column":65}}],"line":233}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"c986f4aa461a262a66f2e1d494dbd8905b8efbeb"} +,"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Resumes.js": {"path":"/home/localuser/RalfAIDemo/MerchantsOfHope-SupplyANdDemandPortal/frontend/src/pages/Resumes.js","statementMap":{"0":{"start":{"line":7,"column":16},"end":{"line":243,"column":1}},"1":{"start":{"line":8,"column":36},"end":{"line":8,"column":51}},"2":{"start":{"line":9,"column":42},"end":{"line":9,"column":56}},"3":{"start":{"line":11,"column":48},"end":{"line":15,"column":4}},"4":{"start":{"line":14,"column":4},"end":{"line":14,"column":14}},"5":{"start":{"line":17,"column":27},"end":{"line":39,"column":3}},"6":{"start":{"line":18,"column":17},"end":{"line":18,"column":34}},"7":{"start":{"line":19,"column":4},"end":{"line":38,"column":5}},"8":{"start":{"line":20,"column":6},"end":{"line":23,"column":7}},"9":{"start":{"line":21,"column":8},"end":{"line":21,"column":56}},"10":{"start":{"line":22,"column":8},"end":{"line":22,"column":15}},"11":{"start":{"line":25,"column":27},"end":{"line":30,"column":7}},"12":{"start":{"line":32,"column":6},"end":{"line":35,"column":7}},"13":{"start":{"line":33,"column":8},"end":{"line":33,"column":70}},"14":{"start":{"line":34,"column":8},"end":{"line":34,"column":15}},"15":{"start":{"line":37,"column":6},"end":{"line":37,"column":28}},"16":{"start":{"line":41,"column":23},"end":{"line":67,"column":3}},"17":{"start":{"line":42,"column":4},"end":{"line":45,"column":5}},"18":{"start":{"line":43,"column":6},"end":{"line":43,"column":42}},"19":{"start":{"line":44,"column":6},"end":{"line":44,"column":13}},"20":{"start":{"line":47,"column":4},"end":{"line":47,"column":23}},"21":{"start":{"line":48,"column":21},"end":{"line":48,"column":35}},"22":{"start":{"line":49,"column":4},"end":{"line":49,"column":44}},"23":{"start":{"line":50,"column":4},"end":{"line":50,"column":75}},"24":{"start":{"line":52,"column":4},"end":{"line":66,"column":5}},"25":{"start":{"line":53,"column":6},"end":{"line":57,"column":9}},"26":{"start":{"line":59,"column":6},"end":{"line":59,"column":53}},"27":{"start":{"line":60,"column":6},"end":{"line":60,"column":28}},"28":{"start":{"line":61,"column":6},"end":{"line":61,"column":16}},"29":{"start":{"line":63,"column":6},"end":{"line":63,"column":76}},"30":{"start":{"line":65,"column":6},"end":{"line":65,"column":26}},"31":{"start":{"line":69,"column":25},"end":{"line":86,"column":3}},"32":{"start":{"line":70,"column":4},"end":{"line":85,"column":5}},"33":{"start":{"line":71,"column":23},"end":{"line":73,"column":8}},"34":{"start":{"line":75,"column":18},"end":{"line":75,"column":71}},"35":{"start":{"line":76,"column":19},"end":{"line":76,"column":46}},"36":{"start":{"line":77,"column":6},"end":{"line":77,"column":22}},"37":{"start":{"line":78,"column":6},"end":{"line":78,"column":116}},"38":{"start":{"line":79,"column":6},"end":{"line":79,"column":38}},"39":{"start":{"line":80,"column":6},"end":{"line":80,"column":19}},"40":{"start":{"line":81,"column":6},"end":{"line":81,"column":20}},"41":{"start":{"line":82,"column":6},"end":{"line":82,"column":38}},"42":{"start":{"line":84,"column":6},"end":{"line":84,"column":47}},"43":{"start":{"line":88,"column":27},"end":{"line":96,"column":3}},"44":{"start":{"line":89,"column":4},"end":{"line":95,"column":5}},"45":{"start":{"line":90,"column":6},"end":{"line":90,"column":58}},"46":{"start":{"line":91,"column":6},"end":{"line":91,"column":47}},"47":{"start":{"line":92,"column":6},"end":{"line":92,"column":16}},"48":{"start":{"line":94,"column":6},"end":{"line":94,"column":50}},"49":{"start":{"line":98,"column":23},"end":{"line":110,"column":3}},"50":{"start":{"line":99,"column":4},"end":{"line":101,"column":5}},"51":{"start":{"line":100,"column":6},"end":{"line":100,"column":13}},"52":{"start":{"line":103,"column":4},"end":{"line":109,"column":5}},"53":{"start":{"line":104,"column":6},"end":{"line":104,"column":53}},"54":{"start":{"line":105,"column":6},"end":{"line":105,"column":52}},"55":{"start":{"line":106,"column":6},"end":{"line":106,"column":16}},"56":{"start":{"line":108,"column":6},"end":{"line":108,"column":45}},"57":{"start":{"line":112,"column":2},"end":{"line":118,"column":3}},"58":{"start":{"line":113,"column":4},"end":{"line":117,"column":6}},"59":{"start":{"line":120,"column":2},"end":{"line":242,"column":4}},"60":{"start":{"line":177,"column":12},"end":{"line":229,"column":18}},"61":{"start":{"line":204,"column":37},"end":{"line":204,"column":62}},"62":{"start":{"line":212,"column":39},"end":{"line":212,"column":66}},"63":{"start":{"line":220,"column":37},"end":{"line":220,"column":60}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":7,"column":16},"end":{"line":7,"column":17}},"loc":{"start":{"line":7,"column":22},"end":{"line":243,"column":1}},"line":7},"1":{"name":"(anonymous_1)","decl":{"start":{"line":11,"column":68},"end":{"line":11,"column":69}},"loc":{"start":{"line":11,"column":80},"end":{"line":15,"column":3}},"line":11},"2":{"name":"(anonymous_2)","decl":{"start":{"line":17,"column":27},"end":{"line":17,"column":28}},"loc":{"start":{"line":17,"column":34},"end":{"line":39,"column":3}},"line":17},"3":{"name":"(anonymous_3)","decl":{"start":{"line":41,"column":23},"end":{"line":41,"column":24}},"loc":{"start":{"line":41,"column":35},"end":{"line":67,"column":3}},"line":41},"4":{"name":"(anonymous_4)","decl":{"start":{"line":69,"column":25},"end":{"line":69,"column":26}},"loc":{"start":{"line":69,"column":45},"end":{"line":86,"column":3}},"line":69},"5":{"name":"(anonymous_5)","decl":{"start":{"line":88,"column":27},"end":{"line":88,"column":28}},"loc":{"start":{"line":88,"column":47},"end":{"line":96,"column":3}},"line":88},"6":{"name":"(anonymous_6)","decl":{"start":{"line":98,"column":23},"end":{"line":98,"column":24}},"loc":{"start":{"line":98,"column":43},"end":{"line":110,"column":3}},"line":98},"7":{"name":"(anonymous_7)","decl":{"start":{"line":176,"column":22},"end":{"line":176,"column":23}},"loc":{"start":{"line":177,"column":12},"end":{"line":229,"column":18}},"line":177},"8":{"name":"(anonymous_8)","decl":{"start":{"line":204,"column":31},"end":{"line":204,"column":32}},"loc":{"start":{"line":204,"column":37},"end":{"line":204,"column":62}},"line":204},"9":{"name":"(anonymous_9)","decl":{"start":{"line":212,"column":33},"end":{"line":212,"column":34}},"loc":{"start":{"line":212,"column":39},"end":{"line":212,"column":66}},"line":212},"10":{"name":"(anonymous_10)","decl":{"start":{"line":220,"column":31},"end":{"line":220,"column":32}},"loc":{"start":{"line":220,"column":37},"end":{"line":220,"column":60}},"line":220}},"branchMap":{"0":{"loc":{"start":{"line":19,"column":4},"end":{"line":38,"column":5}},"type":"if","locations":[{"start":{"line":19,"column":4},"end":{"line":38,"column":5}},{"start":{},"end":{}}],"line":19},"1":{"loc":{"start":{"line":20,"column":6},"end":{"line":23,"column":7}},"type":"if","locations":[{"start":{"line":20,"column":6},"end":{"line":23,"column":7}},{"start":{},"end":{}}],"line":20},"2":{"loc":{"start":{"line":32,"column":6},"end":{"line":35,"column":7}},"type":"if","locations":[{"start":{"line":32,"column":6},"end":{"line":35,"column":7}},{"start":{},"end":{}}],"line":32},"3":{"loc":{"start":{"line":42,"column":4},"end":{"line":45,"column":5}},"type":"if","locations":[{"start":{"line":42,"column":4},"end":{"line":45,"column":5}},{"start":{},"end":{}}],"line":42},"4":{"loc":{"start":{"line":50,"column":33},"end":{"line":50,"column":73}},"type":"cond-expr","locations":[{"start":{"line":50,"column":57},"end":{"line":50,"column":63}},{"start":{"line":50,"column":66},"end":{"line":50,"column":73}}],"line":50},"5":{"loc":{"start":{"line":63,"column":18},"end":{"line":63,"column":74}},"type":"binary-expr","locations":[{"start":{"line":63,"column":18},"end":{"line":63,"column":45}},{"start":{"line":63,"column":49},"end":{"line":63,"column":74}}],"line":63},"6":{"loc":{"start":{"line":78,"column":36},"end":{"line":78,"column":114}},"type":"binary-expr","locations":[{"start":{"line":78,"column":36},"end":{"line":78,"column":98}},{"start":{"line":78,"column":102},"end":{"line":78,"column":114}}],"line":78},"7":{"loc":{"start":{"line":99,"column":4},"end":{"line":101,"column":5}},"type":"if","locations":[{"start":{"line":99,"column":4},"end":{"line":101,"column":5}},{"start":{},"end":{}}],"line":99},"8":{"loc":{"start":{"line":112,"column":2},"end":{"line":118,"column":3}},"type":"if","locations":[{"start":{"line":112,"column":2},"end":{"line":118,"column":3}},{"start":{},"end":{}}],"line":112},"9":{"loc":{"start":{"line":150,"column":13},"end":{"line":168,"column":13}},"type":"binary-expr","locations":[{"start":{"line":150,"column":13},"end":{"line":150,"column":25}},{"start":{"line":151,"column":14},"end":{"line":167,"column":20}}],"line":150},"10":{"loc":{"start":{"line":165,"column":19},"end":{"line":165,"column":56}},"type":"cond-expr","locations":[{"start":{"line":165,"column":31},"end":{"line":165,"column":45}},{"start":{"line":165,"column":48},"end":{"line":165,"column":56}}],"line":165},"11":{"loc":{"start":{"line":175,"column":9},"end":{"line":239,"column":9}},"type":"cond-expr","locations":[{"start":{"line":176,"column":10},"end":{"line":230,"column":12}},{"start":{"line":232,"column":10},"end":{"line":238,"column":16}}],"line":175},"12":{"loc":{"start":{"line":190,"column":25},"end":{"line":198,"column":25}},"type":"binary-expr","locations":[{"start":{"line":190,"column":25},"end":{"line":190,"column":42}},{"start":{"line":191,"column":26},"end":{"line":197,"column":29}}],"line":190},"13":{"loc":{"start":{"line":210,"column":21},"end":{"line":218,"column":21}},"type":"binary-expr","locations":[{"start":{"line":210,"column":21},"end":{"line":210,"column":39}},{"start":{"line":211,"column":22},"end":{"line":217,"column":31}}],"line":210}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"68a4b3d465c9b175cb8f34ec35981e7f95c4a4f4"} +} diff --git a/frontend/coverage/lcov-report/base.css b/frontend/coverage/lcov-report/base.css new file mode 100644 index 0000000..f418035 --- /dev/null +++ b/frontend/coverage/lcov-report/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/frontend/coverage/lcov-report/block-navigation.js b/frontend/coverage/lcov-report/block-navigation.js new file mode 100644 index 0000000..530d1ed --- /dev/null +++ b/frontend/coverage/lcov-report/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selector that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/frontend/coverage/lcov-report/favicon.png b/frontend/coverage/lcov-report/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..c1525b811a167671e9de1fa78aab9f5c0b61cef7 GIT binary patch literal 445 zcmV;u0Yd(XP))rP{nL}Ln%S7`m{0DjX9TLF* zFCb$4Oi7vyLOydb!7n&^ItCzb-%BoB`=x@N2jll2Nj`kauio%aw_@fe&*}LqlFT43 z8doAAe))z_%=P%v^@JHp3Hjhj^6*Kr_h|g_Gr?ZAa&y>wxHE99Gk>A)2MplWz2xdG zy8VD2J|Uf#EAw*bo5O*PO_}X2Tob{%bUoO2G~T`@%S6qPyc}VkhV}UifBuRk>%5v( z)x7B{I~z*k<7dv#5tC+m{km(D087J4O%+<<;K|qwefb6@GSX45wCK}Sn*> + + + + Code coverage report for All files + + + + + + + + + +
+
+

All files

+
+ +
+ 19.1% + Statements + 85/445 +
+ + +
+ 7.73% + Branches + 27/349 +
+ + +
+ 12.69% + Functions + 16/126 +
+ + +
+ 19.72% + Lines + 85/431 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
src +
+
28.57%4/1415.38%2/1366.66%2/328.57%4/14
src/components +
+
76.47%13/1783.33%5/650%4/876.47%13/17
src/contexts +
+
61.4%35/5737.5%3/871.42%5/761.4%35/57
src/lib +
+
0%0/40%0/4100%0/00%0/4
src/pages +
+
9.34%33/3535.34%17/3184.62%5/1089.73%33/339
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/prettify.css b/frontend/coverage/lcov-report/prettify.css new file mode 100644 index 0000000..b317a7c --- /dev/null +++ b/frontend/coverage/lcov-report/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/frontend/coverage/lcov-report/prettify.js b/frontend/coverage/lcov-report/prettify.js new file mode 100644 index 0000000..b322523 --- /dev/null +++ b/frontend/coverage/lcov-report/prettify.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/frontend/coverage/lcov-report/sort-arrow-sprite.png b/frontend/coverage/lcov-report/sort-arrow-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed68316eb3f65dec9063332d2f69bf3093bbfab GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^>_9Bd!3HEZxJ@+%Qh}Z>jv*C{$p!i!8j}?a+@3A= zIAGwzjijN=FBi!|L1t?LM;Q;gkwn>2cAy-KV{dn nf0J1DIvEHQu*n~6U}x}qyky7vi4|9XhBJ7&`njxgN@xNA8m%nc literal 0 HcmV?d00001 diff --git a/frontend/coverage/lcov-report/sorter.js b/frontend/coverage/lcov-report/sorter.js new file mode 100644 index 0000000..4ed70ae --- /dev/null +++ b/frontend/coverage/lcov-report/sorter.js @@ -0,0 +1,210 @@ +/* eslint-disable */ +var addSorting = (function() { + 'use strict'; + var cols, + currentSort = { + index: 0, + desc: false + }; + + // returns the summary table element + function getTable() { + return document.querySelector('.coverage-summary'); + } + // returns the thead element of the summary table + function getTableHeader() { + return getTable().querySelector('thead tr'); + } + // returns the tbody element of the summary table + function getTableBody() { + return getTable().querySelector('tbody'); + } + // returns the th element for nth column + function getNthColumn(n) { + return getTableHeader().querySelectorAll('th')[n]; + } + + function onFilterInput() { + const searchValue = document.getElementById('fileSearch').value; + const rows = document.getElementsByTagName('tbody')[0].children; + + // Try to create a RegExp from the searchValue. If it fails (invalid regex), + // it will be treated as a plain text search + let searchRegex; + try { + searchRegex = new RegExp(searchValue, 'i'); // 'i' for case-insensitive + } catch (error) { + searchRegex = null; + } + + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + let isMatch = false; + + if (searchRegex) { + // If a valid regex was created, use it for matching + isMatch = searchRegex.test(row.textContent); + } else { + // Otherwise, fall back to the original plain text search + isMatch = row.textContent + .toLowerCase() + .includes(searchValue.toLowerCase()); + } + + row.style.display = isMatch ? '' : 'none'; + } + } + + // loads the search box + function addSearchBox() { + var template = document.getElementById('filterTemplate'); + var templateClone = template.content.cloneNode(true); + templateClone.getElementById('fileSearch').oninput = onFilterInput; + template.parentElement.appendChild(templateClone); + } + + // loads all columns + function loadColumns() { + var colNodes = getTableHeader().querySelectorAll('th'), + colNode, + cols = [], + col, + i; + + for (i = 0; i < colNodes.length; i += 1) { + colNode = colNodes[i]; + col = { + key: colNode.getAttribute('data-col'), + sortable: !colNode.getAttribute('data-nosort'), + type: colNode.getAttribute('data-type') || 'string' + }; + cols.push(col); + if (col.sortable) { + col.defaultDescSort = col.type === 'number'; + colNode.innerHTML = + colNode.innerHTML + ''; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function(a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function(a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc + ? ' sorted-desc' + : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function() { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i = 0; i < cols.length; i += 1) { + if (cols[i].sortable) { + // add the click event handler on the th so users + // dont have to click on those tiny arrows + el = getNthColumn(i).querySelector('.sorter').parentElement; + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function() { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(); + addSearchBox(); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/frontend/coverage/lcov-report/src/App.js.html b/frontend/coverage/lcov-report/src/App.js.html new file mode 100644 index 0000000..3937e97 --- /dev/null +++ b/frontend/coverage/lcov-report/src/App.js.html @@ -0,0 +1,493 @@ + + + + + + Code coverage report for src/App.js + + + + + + + + + +
+
+

All files / src App.js

+
+ +
+ 33.33% + Statements + 4/12 +
+ + +
+ 15.38% + Branches + 2/13 +
+ + +
+ 66.66% + Functions + 2/3 +
+ + +
+ 33.33% + Lines + 4/12 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import React from 'react';
+import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
+import { QueryClient, QueryClientProvider } from 'react-query';
+import { Toaster } from 'react-hot-toast';
+import { AuthProvider, useAuth } from './contexts/AuthContext';
+import Layout from './components/Layout';
+import Login from './pages/Login';
+import Register from './pages/Register';
+import Dashboard from './pages/Dashboard';
+import Jobs from './pages/Jobs';
+import JobDetails from './pages/JobDetails';
+import CreateJob from './pages/CreateJob';
+import Candidates from './pages/Candidates';
+import CandidateDetails from './pages/CandidateDetails';
+import Applications from './pages/Applications';
+import Profile from './pages/Profile';
+import Employers from './pages/Employers';
+import EmployerDetails from './pages/EmployerDetails';
+import Resumes from './pages/Resumes';
+ 
+const queryClient = new QueryClient();
+ 
+function ProtectedRoute({ children, allowedRoles = [] }) {
+  const { user, loading } = useAuth();
+ 
+  if (loading) {
+    return (
+      <div className="min-h-screen flex items-center justify-center">
+        <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-primary-600"></div>
+      </div>
+    );
+  }
+ 
+  if (!user) {
+    return <Navigate to="/login" replace />;
+  }
+ 
+  if (allowedRoles.length > 0 && !allowedRoles.includes(user.role)) {
+    return <Navigate to="/dashboard" replace />;
+  }
+ 
+  return children;
+}
+ 
+function AppRoutes() {
+  const { user } = useAuth();
+ 
+  return (
+    <Routes>
+      <Route path="/login" element={!user ? <Login /> : <Navigate to="/dashboard" replace />} />
+      <Route path="/register" element={!user ? <Register /> : <Navigate to="/dashboard" replace />} />
+      
+      <Route path="/" element={<Layout />}>
+        <Route index element={<Navigate to="/dashboard" replace />} />
+        <Route path="dashboard" element={
+          <ProtectedRoute>
+            <Dashboard />
+          </ProtectedRoute>
+        } />
+        
+        <Route path="jobs" element={
+          <ProtectedRoute>
+            <Jobs />
+          </ProtectedRoute>
+        } />
+        <Route path="jobs/create" element={
+          <ProtectedRoute allowedRoles={['employer', 'recruiter']}>
+            <CreateJob />
+          </ProtectedRoute>
+        } />
+        <Route path="jobs/:id" element={
+          <ProtectedRoute>
+            <JobDetails />
+          </ProtectedRoute>
+        } />
+        
+        <Route path="candidates" element={
+          <ProtectedRoute allowedRoles={['admin', 'recruiter', 'employer']}>
+            <Candidates />
+          </ProtectedRoute>
+        } />
+        <Route path="candidates/:id" element={
+          <ProtectedRoute allowedRoles={['admin', 'recruiter', 'employer']}>
+            <CandidateDetails />
+          </ProtectedRoute>
+        } />
+        
+        <Route path="applications" element={
+          <ProtectedRoute>
+            <Applications />
+          </ProtectedRoute>
+        } />
+        
+        <Route path="employers" element={
+          <ProtectedRoute allowedRoles={['admin', 'recruiter']}>
+            <Employers />
+          </ProtectedRoute>
+        } />
+        <Route path="employers/:id" element={
+          <ProtectedRoute allowedRoles={['admin', 'recruiter']}>
+            <EmployerDetails />
+          </ProtectedRoute>
+        } />
+        
+        <Route path="resumes" element={
+          <ProtectedRoute allowedRoles={['candidate']}>
+            <Resumes />
+          </ProtectedRoute>
+        } />
+        
+        <Route path="profile" element={
+          <ProtectedRoute>
+            <Profile />
+          </ProtectedRoute>
+        } />
+      </Route>
+    </Routes>
+  );
+}
+ 
+function App() {
+  return (
+    <QueryClientProvider client={queryClient}>
+      <AuthProvider>
+        <Router>
+          <div className="App">
+            <AppRoutes />
+            <Toaster position="top-right" />
+          </div>
+        </Router>
+      </AuthProvider>
+    </QueryClientProvider>
+  );
+}
+ 
+export default App;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/components/Layout.js.html b/frontend/coverage/lcov-report/src/components/Layout.js.html new file mode 100644 index 0000000..bc1b069 --- /dev/null +++ b/frontend/coverage/lcov-report/src/components/Layout.js.html @@ -0,0 +1,643 @@ + + + + + + Code coverage report for src/components/Layout.js + + + + + + + + + +
+
+

All files / src/components Layout.js

+
+ +
+ 76.47% + Statements + 13/17 +
+ + +
+ 83.33% + Branches + 5/6 +
+ + +
+ 50% + Functions + 4/8 +
+ + +
+ 76.47% + Lines + 13/17 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +3x +3x +3x +  +3x +  +  +  +  +  +  +  +  +3x +18x +  +  +3x +  +  +  +3x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +12x +12x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +12x +12x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import React, { useState } from 'react';
+import { Link, useLocation, Outlet } from 'react-router-dom';
+import { useAuth } from '../contexts/AuthContext';
+import {
+  Home,
+  Briefcase,
+  Users,
+  FileText,
+  Building,
+  User,
+  LogOut,
+  Menu,
+  X,
+  Bell
+} from 'lucide-react';
+ 
+const Layout = () => {
+  const { user, logout } = useAuth();
+  const location = useLocation();
+  const [sidebarOpen, setSidebarOpen] = useState(false);
+ 
+  const navigation = [
+    { name: 'Dashboard', href: '/dashboard', icon: Home, roles: ['admin', 'recruiter', 'employer', 'candidate'] },
+    { name: 'Jobs', href: '/jobs', icon: Briefcase, roles: ['admin', 'recruiter', 'employer', 'candidate'] },
+    { name: 'Candidates', href: '/candidates', icon: Users, roles: ['admin', 'recruiter', 'employer'] },
+    { name: 'Applications', href: '/applications', icon: FileText, roles: ['admin', 'recruiter', 'employer', 'candidate'] },
+    { name: 'Employers', href: '/employers', icon: Building, roles: ['admin', 'recruiter'] },
+    { name: 'Resumes', href: '/resumes', icon: FileText, roles: ['candidate'] },
+  ];
+ 
+  const filteredNavigation = navigation.filter(item => 
+    item.roles.includes(user?.role)
+  );
+ 
+  const handleLogout = () => {
+    logout();
+  };
+ 
+  return (
+    <div className="min-h-screen bg-gray-50">
+      {/* Mobile sidebar */}
+      <div className={`fixed inset-0 z-50 lg:hidden ${sidebarOpen ? 'block' : 'hidden'}`}>
+        <div className="fixed inset-0 bg-gray-600 bg-opacity-75" onClick={() => setSidebarOpen(false)} />
+        <div className="relative flex-1 flex flex-col max-w-xs w-full bg-white">
+          <div className="absolute top-0 right-0 -mr-12 pt-2">
+            <button
+              type="button"
+              className="ml-1 flex items-center justify-center h-10 w-10 rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
+              onClick={() => setSidebarOpen(false)}
+            >
+              <X className="h-6 w-6 text-white" />
+            </button>
+          </div>
+          <div className="flex-1 h-0 pt-5 pb-4 overflow-y-auto">
+            <div className="flex-shrink-0 flex items-center px-4">
+              <h1 className="text-xl font-bold text-gray-900">MerchantsOfHope-SupplyANdDemandPortal</h1>
+            </div>
+            <nav className="mt-5 px-2 space-y-1">
+              {filteredNavigation.map((item) => {
+                const isActive = location.pathname === item.href;
+                return (
+                  <Link
+                    key={item.name}
+                    to={item.href}
+                    className={`${
+                      isActive
+                        ? 'bg-primary-100 text-primary-900'
+                        : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'
+                    } group flex items-center px-2 py-2 text-base font-medium rounded-md`}
+                  >
+                    <item.icon className="mr-4 h-6 w-6" />
+                    {item.name}
+                  </Link>
+                );
+              })}
+            </nav>
+          </div>
+        </div>
+      </div>
+ 
+      {/* Desktop sidebar */}
+      <div className="hidden lg:flex lg:w-64 lg:flex-col lg:fixed lg:inset-y-0">
+        <div className="flex-1 flex flex-col min-h-0 border-r border-gray-200 bg-white">
+          <div className="flex-1 flex flex-col pt-5 pb-4 overflow-y-auto">
+            <div className="flex items-center flex-shrink-0 px-4">
+              <h1 className="text-xl font-bold text-gray-900">MerchantsOfHope-SupplyANdDemandPortal</h1>
+            </div>
+            <nav className="mt-5 flex-1 px-2 space-y-1">
+              {filteredNavigation.map((item) => {
+                const isActive = location.pathname === item.href;
+                return (
+                  <Link
+                    key={item.name}
+                    to={item.href}
+                    className={`${
+                      isActive
+                        ? 'bg-primary-100 text-primary-900'
+                        : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'
+                    } group flex items-center px-2 py-2 text-sm font-medium rounded-md`}
+                  >
+                    <item.icon className="mr-3 h-6 w-6" />
+                    {item.name}
+                  </Link>
+                );
+              })}
+            </nav>
+          </div>
+          <div className="flex-shrink-0 flex border-t border-gray-200 p-4">
+            <div className="flex-shrink-0 w-full group block">
+              <div className="flex items-center">
+                <div className="ml-3">
+                  <p className="text-sm font-medium text-gray-700 group-hover:text-gray-900">
+                    {user?.firstName} {user?.lastName}
+                  </p>
+                  <p className="text-xs font-medium text-gray-500 group-hover:text-gray-700">
+                    {user?.role}
+                  </p>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+ 
+      {/* Main content */}
+      <div className="lg:pl-64 flex flex-col flex-1">
+        <div className="sticky top-0 z-10 lg:hidden pl-1 pt-1 sm:pl-3 sm:pt-3 bg-gray-100">
+          <button
+            type="button"
+            className="-ml-0.5 -mt-0.5 h-12 w-12 inline-flex items-center justify-center rounded-md text-gray-500 hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-primary-500"
+            onClick={() => setSidebarOpen(true)}
+          >
+            <Menu className="h-6 w-6" />
+          </button>
+        </div>
+ 
+        <main className="flex-1">
+          <div className="py-6">
+            <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
+              <Outlet />
+            </div>
+          </div>
+        </main>
+      </div>
+ 
+      {/* Top bar for desktop */}
+      <div className="hidden lg:block lg:pl-64">
+        <div className="sticky top-0 z-10 flex-shrink-0 flex h-16 bg-white border-b border-gray-200">
+          <div className="flex-1 px-4 flex justify-between">
+            <div className="flex-1 flex">
+              <div className="w-full flex md:ml-0">
+                <div className="relative w-full text-gray-400 focus-within:text-gray-600">
+                  <div className="absolute inset-y-0 left-0 flex items-center pointer-events-none">
+                    <Bell className="h-5 w-5" />
+                  </div>
+                </div>
+              </div>
+            </div>
+            <div className="ml-4 flex items-center md:ml-6">
+              <div className="ml-3 relative">
+                <div className="flex items-center space-x-4">
+                  <Link
+                    to="/profile"
+                    className="flex items-center text-sm font-medium text-gray-700 hover:text-gray-900"
+                  >
+                    <User className="h-5 w-5 mr-2" />
+                    Profile
+                  </Link>
+                  <button
+                    onClick={handleLogout}
+                    className="flex items-center text-sm font-medium text-gray-700 hover:text-gray-900"
+                  >
+                    <LogOut className="h-5 w-5 mr-2" />
+                    Logout
+                  </button>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};
+ 
+export default Layout;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/components/index.html b/frontend/coverage/lcov-report/src/components/index.html new file mode 100644 index 0000000..ecdca4d --- /dev/null +++ b/frontend/coverage/lcov-report/src/components/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/components + + + + + + + + + +
+
+

All files src/components

+
+ +
+ 76.47% + Statements + 13/17 +
+ + +
+ 83.33% + Branches + 5/6 +
+ + +
+ 50% + Functions + 4/8 +
+ + +
+ 76.47% + Lines + 13/17 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
Layout.js +
+
76.47%13/1783.33%5/650%4/876.47%13/17
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/contexts/AuthContext.js.html b/frontend/coverage/lcov-report/src/contexts/AuthContext.js.html new file mode 100644 index 0000000..d17f54b --- /dev/null +++ b/frontend/coverage/lcov-report/src/contexts/AuthContext.js.html @@ -0,0 +1,400 @@ + + + + + + Code coverage report for src/contexts/AuthContext.js + + + + + + + + + +
+
+

All files / src/contexts AuthContext.js

+
+ +
+ 61.4% + Statements + 35/57 +
+ + +
+ 37.5% + Branches + 3/8 +
+ + +
+ 71.42% + Functions + 5/7 +
+ + +
+ 61.4% + Lines + 35/57 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106  +  +  +  +1x +  +1x +9x +9x +  +  +9x +  +  +1x +9x +9x +  +9x +3x +3x +  +  +  +3x +  +  +  +9x +  +  +  +  +  +  +  +  +  +  +  +  +9x +3x +3x +2x +  +2x +2x +2x +  +2x +2x +  +1x +1x +1x +  +  +  +9x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +9x +1x +1x +  +  +  +1x +1x +1x +1x +  +  +  +9x +  +  +  +  +  +  +  +  +9x +  +  +  +  +  + 
import React, { createContext, useContext, useState, useEffect } from 'react';
+import axios from 'axios';
+import toast from 'react-hot-toast';
+ 
+const AuthContext = createContext();
+ 
+export const useAuth = () => {
+  const context = useContext(AuthContext);
+  Iif (!context) {
+    throw new Error('useAuth must be used within an AuthProvider');
+  }
+  return context;
+};
+ 
+export const AuthProvider = ({ children }) => {
+  const [user, setUser] = useState(null);
+  const [loading, setLoading] = useState(true);
+ 
+  useEffect(() => {
+    const token = localStorage.getItem('token');
+    Iif (token) {
+      axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
+      fetchUser();
+    } else {
+      setLoading(false);
+    }
+  }, []);
+ 
+  const fetchUser = async () => {
+    try {
+      const response = await axios.get('/api/auth/me');
+      setUser(response.data.user);
+    } catch (error) {
+      console.error('Failed to fetch user:', error);
+      localStorage.removeItem('token');
+      delete axios.defaults.headers.common['Authorization'];
+    } finally {
+      setLoading(false);
+    }
+  };
+ 
+  const login = async (email, password) => {
+    try {
+      const response = await axios.post('/api/auth/login', { email, password });
+      const { token, user } = response.data;
+      
+      localStorage.setItem('token', token);
+      axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
+      setUser(user);
+      
+      toast.success('Login successful!');
+      return { success: true };
+    } catch (error) {
+      const message = error.response?.data?.error || 'Login failed';
+      toast.error(message);
+      return { success: false, error: message };
+    }
+  };
+ 
+  const register = async (userData) => {
+    try {
+      const response = await axios.post('/api/auth/register', userData);
+      const { token, user } = response.data;
+      
+      localStorage.setItem('token', token);
+      axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
+      setUser(user);
+      
+      toast.success('Registration successful!');
+      return { success: true };
+    } catch (error) {
+      const message = error.response?.data?.error || 'Registration failed';
+      toast.error(message);
+      return { success: false, error: message };
+    }
+  };
+ 
+  const logout = async () => {
+    try {
+      await axios.post('/api/auth/logout');
+    } catch (error) {
+      console.error('Logout error:', error);
+    } finally {
+      localStorage.removeItem('token');
+      delete axios.defaults.headers.common['Authorization'];
+      setUser(null);
+      toast.success('Logged out successfully');
+    }
+  };
+ 
+  const value = {
+    user,
+    loading,
+    login,
+    register,
+    logout,
+    fetchUser
+  };
+ 
+  return (
+    <AuthContext.Provider value={value}>
+      {children}
+    </AuthContext.Provider>
+  );
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/contexts/index.html b/frontend/coverage/lcov-report/src/contexts/index.html new file mode 100644 index 0000000..06b7513 --- /dev/null +++ b/frontend/coverage/lcov-report/src/contexts/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/contexts + + + + + + + + + +
+
+

All files src/contexts

+
+ +
+ 61.4% + Statements + 35/57 +
+ + +
+ 37.5% + Branches + 3/8 +
+ + +
+ 71.42% + Functions + 5/7 +
+ + +
+ 61.4% + Lines + 35/57 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
AuthContext.js +
+
61.4%35/5737.5%3/871.42%5/761.4%35/57
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/index.html b/frontend/coverage/lcov-report/src/index.html new file mode 100644 index 0000000..1304d72 --- /dev/null +++ b/frontend/coverage/lcov-report/src/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for src + + + + + + + + + +
+
+

All files src

+
+ +
+ 28.57% + Statements + 4/14 +
+ + +
+ 15.38% + Branches + 2/13 +
+ + +
+ 66.66% + Functions + 2/3 +
+ + +
+ 28.57% + Lines + 4/14 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
App.js +
+
33.33%4/1215.38%2/1366.66%2/333.33%4/12
index.js +
+
0%0/2100%0/0100%0/00%0/2
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/index.js.html b/frontend/coverage/lcov-report/src/index.js.html new file mode 100644 index 0000000..49f1f94 --- /dev/null +++ b/frontend/coverage/lcov-report/src/index.js.html @@ -0,0 +1,121 @@ + + + + + + Code coverage report for src/index.js + + + + + + + + + +
+
+

All files / src index.js

+
+ +
+ 0% + Statements + 0/2 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 0% + Lines + 0/2 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13  +  +  +  +  +  +  +  +  +  +  +  + 
import React from 'react';
+import ReactDOM from 'react-dom/client';
+import './index.css';
+import './lib/configureAxios';
+import App from './App';
+ 
+const root = ReactDOM.createRoot(document.getElementById('root'));
+root.render(
+  <React.StrictMode>
+    <App />
+  </React.StrictMode>
+);
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/lib/configureAxios.js.html b/frontend/coverage/lcov-report/src/lib/configureAxios.js.html new file mode 100644 index 0000000..5aab490 --- /dev/null +++ b/frontend/coverage/lcov-report/src/lib/configureAxios.js.html @@ -0,0 +1,118 @@ + + + + + + Code coverage report for src/lib/configureAxios.js + + + + + + + + + +
+
+

All files / src/lib configureAxios.js

+
+ +
+ 0% + Statements + 0/4 +
+ + +
+ 0% + Branches + 0/4 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 0% + Lines + 0/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12  +  +  +  +  +  +  +  +  +  +  + 
import axios from 'axios';
+ 
+const baseURL = process.env.REACT_APP_API_URL || '';
+ 
+if (baseURL) {
+  axios.defaults.baseURL = baseURL;
+}
+ 
+axios.defaults.headers.common['Content-Type'] = 'application/json';
+ 
+export default axios;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/lib/index.html b/frontend/coverage/lcov-report/src/lib/index.html new file mode 100644 index 0000000..1a02e9a --- /dev/null +++ b/frontend/coverage/lcov-report/src/lib/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/lib + + + + + + + + + +
+
+

All files src/lib

+
+ +
+ 0% + Statements + 0/4 +
+ + +
+ 0% + Branches + 0/4 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 0% + Lines + 0/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
configureAxios.js +
+
0%0/40%0/4100%0/00%0/4
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/pages/Applications.js.html b/frontend/coverage/lcov-report/src/pages/Applications.js.html new file mode 100644 index 0000000..21f7206 --- /dev/null +++ b/frontend/coverage/lcov-report/src/pages/Applications.js.html @@ -0,0 +1,418 @@ + + + + + + Code coverage report for src/pages/Applications.js + + + + + + + + + +
+
+

All files / src/pages Applications.js

+
+ +
+ 3.7% + Statements + 1/27 +
+ + +
+ 0% + Branches + 0/23 +
+ + +
+ 0% + Functions + 0/5 +
+ + +
+ 3.7% + Lines + 1/27 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import React from 'react';
+import { useQuery } from 'react-query';
+import axios from 'axios';
+import { FileText, Building, Clock, CheckCircle, XCircle, AlertCircle } from 'lucide-react';
+ 
+const Applications = () => {
+  const { data, isLoading } = useQuery('applications', async () => {
+    const response = await axios.get('/api/applications');
+    return response.data;
+  });
+ 
+  const getStatusIcon = (status) => {
+    switch (status) {
+      case 'applied': return <Clock className="h-4 w-4" />;
+      case 'reviewed': return <AlertCircle className="h-4 w-4" />;
+      case 'shortlisted': return <CheckCircle className="h-4 w-4" />;
+      case 'interviewed': return <CheckCircle className="h-4 w-4" />;
+      case 'offered': return <CheckCircle className="h-4 w-4" />;
+      case 'rejected': return <XCircle className="h-4 w-4" />;
+      default: return <Clock className="h-4 w-4" />;
+    }
+  };
+ 
+  const getStatusColor = (status) => {
+    switch (status) {
+      case 'applied': return 'bg-blue-100 text-blue-800';
+      case 'reviewed': return 'bg-yellow-100 text-yellow-800';
+      case 'shortlisted': return 'bg-green-100 text-green-800';
+      case 'interviewed': return 'bg-green-100 text-green-800';
+      case 'offered': return 'bg-green-100 text-green-800';
+      case 'rejected': return 'bg-red-100 text-red-800';
+      case 'withdrawn': return 'bg-gray-100 text-gray-800';
+      default: return 'bg-gray-100 text-gray-800';
+    }
+  };
+ 
+  if (isLoading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-primary-600"></div>
+      </div>
+    );
+  }
+ 
+  return (
+    <div className="space-y-6">
+      <div>
+        <h1 className="text-2xl font-bold text-gray-900">Applications</h1>
+        <p className="mt-1 text-sm text-gray-500">
+          Track your job applications and their status
+        </p>
+      </div>
+ 
+      <div className="space-y-4">
+        {data?.applications?.length > 0 ? (
+          data.applications.map((application) => (
+            <div key={application.id} className="bg-white shadow rounded-lg">
+              <div className="px-4 py-5 sm:p-6">
+                <div className="flex items-start justify-between">
+                  <div className="flex-1">
+                    <div className="flex items-center">
+                      <h3 className="text-lg font-medium text-gray-900">
+                        {application.job_title}
+                      </h3>
+                      <span className={`ml-3 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getStatusColor(application.status)}`}>
+                        {getStatusIcon(application.status)}
+                        <span className="ml-1 capitalize">{application.status}</span>
+                      </span>
+                    </div>
+                    <div className="mt-1 flex items-center text-sm text-gray-500">
+                      <Building className="h-4 w-4 mr-1" />
+                      {application.company_name}
+                    </div>
+                    <div className="mt-2 flex items-center text-sm text-gray-500">
+                      <Clock className="h-4 w-4 mr-1" />
+                      Applied on {new Date(application.applied_at).toLocaleDateString()}
+                    </div>
+                    {application.cover_letter && (
+                      <div className="mt-3">
+                        <p className="text-sm text-gray-600 line-clamp-2">
+                          {application.cover_letter}
+                        </p>
+                      </div>
+                    )}
+                    {application.notes && (
+                      <div className="mt-3">
+                        <p className="text-sm text-gray-600">
+                          <strong>Notes:</strong> {application.notes}
+                        </p>
+                      </div>
+                    )}
+                  </div>
+                </div>
+              </div>
+            </div>
+          ))
+        ) : (
+          <div className="text-center py-12">
+            <FileText className="mx-auto h-12 w-12 text-gray-400" />
+            <h3 className="mt-2 text-sm font-medium text-gray-900">No applications found</h3>
+            <p className="mt-1 text-sm text-gray-500">
+              You haven't applied to any jobs yet.
+            </p>
+          </div>
+        )}
+      </div>
+    </div>
+  );
+};
+ 
+export default Applications;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/pages/CandidateDetails.js.html b/frontend/coverage/lcov-report/src/pages/CandidateDetails.js.html new file mode 100644 index 0000000..f15361b --- /dev/null +++ b/frontend/coverage/lcov-report/src/pages/CandidateDetails.js.html @@ -0,0 +1,532 @@ + + + + + + Code coverage report for src/pages/CandidateDetails.js + + + + + + + + + +
+
+

All files / src/pages CandidateDetails.js

+
+ +
+ 9.09% + Statements + 1/11 +
+ + +
+ 0% + Branches + 0/23 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 9.09% + Lines + 1/11 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import React from 'react';
+import { useParams } from 'react-router-dom';
+import { useQuery } from 'react-query';
+import axios from 'axios';
+import { MapPin, Phone, Linkedin, Github, Globe, User } from 'lucide-react';
+ 
+const CandidateDetails = () => {
+  const { id } = useParams();
+ 
+  const { data: candidate, isLoading } = useQuery(['candidate', id], async () => {
+    const response = await axios.get(`/api/candidates/${id}`);
+    return response.data;
+  });
+ 
+  if (isLoading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-primary-600"></div>
+      </div>
+    );
+  }
+ 
+  if (!candidate) {
+    return (
+      <div className="text-center py-12">
+        <h3 className="text-lg font-medium text-gray-900">Candidate not found</h3>
+        <p className="mt-1 text-sm text-gray-500">
+          The candidate you're looking for doesn't exist.
+        </p>
+      </div>
+    );
+  }
+ 
+  return (
+    <div className="space-y-6">
+      <div className="bg-white shadow rounded-lg">
+        <div className="px-4 py-5 sm:p-6">
+          <div className="flex items-start">
+            <div className="flex-shrink-0">
+              <div className="h-16 w-16 rounded-full bg-primary-100 flex items-center justify-center">
+                <User className="h-8 w-8 text-primary-600" />
+              </div>
+            </div>
+            <div className="ml-6 flex-1">
+              <h1 className="text-2xl font-bold text-gray-900">
+                {candidate.first_name} {candidate.last_name}
+              </h1>
+              <p className="text-lg text-gray-600">{candidate.email}</p>
+              
+              <div className="mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2">
+                {candidate.location && (
+                  <div className="flex items-center text-sm text-gray-500">
+                    <MapPin className="h-4 w-4 mr-2" />
+                    {candidate.location}
+                  </div>
+                )}
+                
+                {candidate.phone && (
+                  <div className="flex items-center text-sm text-gray-500">
+                    <Phone className="h-4 w-4 mr-2" />
+                    {candidate.phone}
+                  </div>
+                )}
+                
+                {candidate.linkedin_url && (
+                  <div className="flex items-center text-sm text-gray-500">
+                    <Linkedin className="h-4 w-4 mr-2" />
+                    <a href={candidate.linkedin_url} target="_blank" rel="noopener noreferrer" className="text-primary-600 hover:text-primary-500">
+                      LinkedIn Profile
+                    </a>
+                  </div>
+                )}
+                
+                {candidate.github_url && (
+                  <div className="flex items-center text-sm text-gray-500">
+                    <Github className="h-4 w-4 mr-2" />
+                    <a href={candidate.github_url} target="_blank" rel="noopener noreferrer" className="text-primary-600 hover:text-primary-500">
+                      GitHub Profile
+                    </a>
+                  </div>
+                )}
+                
+                {candidate.portfolio_url && (
+                  <div className="flex items-center text-sm text-gray-500">
+                    <Globe className="h-4 w-4 mr-2" />
+                    <a href={candidate.portfolio_url} target="_blank" rel="noopener noreferrer" className="text-primary-600 hover:text-primary-500">
+                      Portfolio
+                    </a>
+                  </div>
+                )}
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+ 
+      {candidate.bio && (
+        <div className="bg-white shadow rounded-lg">
+          <div className="px-4 py-5 sm:p-6">
+            <h2 className="text-lg font-medium text-gray-900 mb-4">About</h2>
+            <p className="text-gray-600 whitespace-pre-wrap">{candidate.bio}</p>
+          </div>
+        </div>
+      )}
+ 
+      {candidate.skills && candidate.skills.length > 0 && (
+        <div className="bg-white shadow rounded-lg">
+          <div className="px-4 py-5 sm:p-6">
+            <h2 className="text-lg font-medium text-gray-900 mb-4">Skills</h2>
+            <div className="flex flex-wrap gap-2">
+              {candidate.skills.map((skill, index) => (
+                <span
+                  key={index}
+                  className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-primary-100 text-primary-800"
+                >
+                  {skill}
+                </span>
+              ))}
+            </div>
+          </div>
+        </div>
+      )}
+ 
+      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
+        <div className="bg-white shadow rounded-lg">
+          <div className="px-4 py-5 sm:p-6">
+            <h2 className="text-lg font-medium text-gray-900 mb-4">Experience Level</h2>
+            <span className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-primary-100 text-primary-800">
+              {candidate.experience_level || 'Not specified'}
+            </span>
+          </div>
+        </div>
+ 
+        {candidate.salary_expectation && (
+          <div className="bg-white shadow rounded-lg">
+            <div className="px-4 py-5 sm:p-6">
+              <h2 className="text-lg font-medium text-gray-900 mb-4">Salary Expectation</h2>
+              <p className="text-lg font-medium text-gray-900">
+                ${candidate.salary_expectation.toLocaleString()}
+              </p>
+            </div>
+          </div>
+        )}
+      </div>
+    </div>
+  );
+};
+ 
+export default CandidateDetails;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/pages/Candidates.js.html b/frontend/coverage/lcov-report/src/pages/Candidates.js.html new file mode 100644 index 0000000..a85b8ca --- /dev/null +++ b/frontend/coverage/lcov-report/src/pages/Candidates.js.html @@ -0,0 +1,916 @@ + + + + + + Code coverage report for src/pages/Candidates.js + + + + + + + + + +
+
+

All files / src/pages Candidates.js

+
+ +
+ 3.7% + Statements + 1/27 +
+ + +
+ 0% + Branches + 0/28 +
+ + +
+ 0% + Functions + 0/8 +
+ + +
+ 3.84% + Lines + 1/26 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import React, { useState } from 'react';
+import { Link } from 'react-router-dom';
+import { useQuery } from 'react-query';
+import axios from 'axios';
+import { Search, MapPin, User, Star } from 'lucide-react';
+ 
+const Candidates = () => {
+  const [filters, setFilters] = useState({
+    search: '',
+    location: '',
+    experienceLevel: '',
+    skills: ''
+  });
+ 
+  const { data, isLoading, refetch } = useQuery(['candidates', filters], async () => {
+    const params = new URLSearchParams();
+    Object.entries(filters).forEach(([key, value]) => {
+      if (value) params.append(key, value);
+    });
+    
+    const response = await axios.get(`/api/candidates?${params.toString()}`);
+    return response.data;
+  });
+ 
+  const handleFilterChange = (e) => {
+    setFilters({
+      ...filters,
+      [e.target.name]: e.target.value
+    });
+  };
+ 
+  const handleSearch = (e) => {
+    e.preventDefault();
+    refetch();
+  };
+ 
+  const getExperienceColor = (level) => {
+    switch (level) {
+      case 'entry': return 'bg-green-100 text-green-800';
+      case 'mid': return 'bg-blue-100 text-blue-800';
+      case 'senior': return 'bg-purple-100 text-purple-800';
+      case 'lead': return 'bg-orange-100 text-orange-800';
+      case 'executive': return 'bg-red-100 text-red-800';
+      default: return 'bg-gray-100 text-gray-800';
+    }
+  };
+ 
+  if (isLoading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-primary-600"></div>
+      </div>
+    );
+  }
+ 
+  return (
+    <div className="space-y-6">
+      <div>
+        <h1 className="text-2xl font-bold text-gray-900">Candidates</h1>
+        <p className="mt-1 text-sm text-gray-500">
+          Browse and discover talented candidates
+        </p>
+      </div>
+ 
+      {/* Filters */}
+      <div className="bg-white shadow rounded-lg">
+        <div className="px-4 py-5 sm:p-6">
+          <form onSubmit={handleSearch} className="space-y-4">
+            <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
+              <div>
+                <label htmlFor="search" className="block text-sm font-medium text-gray-700">
+                  Search
+                </label>
+                <div className="mt-1 relative">
+                  <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
+                    <Search className="h-5 w-5 text-gray-400" />
+                  </div>
+                  <input
+                    type="text"
+                    name="search"
+                    id="search"
+                    className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
+                    placeholder="Name or keywords"
+                    value={filters.search}
+                    onChange={handleFilterChange}
+                  />
+                </div>
+              </div>
+ 
+              <div>
+                <label htmlFor="location" className="block text-sm font-medium text-gray-700">
+                  Location
+                </label>
+                <div className="mt-1 relative">
+                  <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
+                    <MapPin className="h-5 w-5 text-gray-400" />
+                  </div>
+                  <input
+                    type="text"
+                    name="location"
+                    id="location"
+                    className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
+                    placeholder="City, state, or country"
+                    value={filters.location}
+                    onChange={handleFilterChange}
+                  />
+                </div>
+              </div>
+ 
+              <div>
+                <label htmlFor="experienceLevel" className="block text-sm font-medium text-gray-700">
+                  Experience Level
+                </label>
+                <select
+                  id="experienceLevel"
+                  name="experienceLevel"
+                  className="mt-1 block w-full pl-3 pr-10 py-2 text-base border border-gray-300 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm rounded-md"
+                  value={filters.experienceLevel}
+                  onChange={handleFilterChange}
+                >
+                  <option value="">All Levels</option>
+                  <option value="entry">Entry Level</option>
+                  <option value="mid">Mid Level</option>
+                  <option value="senior">Senior Level</option>
+                  <option value="lead">Lead</option>
+                  <option value="executive">Executive</option>
+                </select>
+              </div>
+ 
+              <div>
+                <label htmlFor="skills" className="block text-sm font-medium text-gray-700">
+                  Skills
+                </label>
+                <input
+                  type="text"
+                  name="skills"
+                  id="skills"
+                  className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
+                  placeholder="JavaScript, React, etc."
+                  value={filters.skills}
+                  onChange={handleFilterChange}
+                />
+              </div>
+            </div>
+ 
+            <div className="flex justify-end">
+              <button
+                type="submit"
+                className="btn btn-primary"
+              >
+                Search Candidates
+              </button>
+            </div>
+          </form>
+        </div>
+      </div>
+ 
+      {/* Candidates List */}
+      <div className="grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3">
+        {data?.candidates?.length > 0 ? (
+          data.candidates.map((candidate) => (
+            <div key={candidate.id} className="bg-white shadow rounded-lg">
+              <div className="px-4 py-5 sm:p-6">
+                <div className="flex items-start">
+                  <div className="flex-shrink-0">
+                    <div className="h-12 w-12 rounded-full bg-primary-100 flex items-center justify-center">
+                      <User className="h-6 w-6 text-primary-600" />
+                    </div>
+                  </div>
+                  <div className="ml-4 flex-1">
+                    <h3 className="text-lg font-medium text-gray-900">
+                      <Link to={`/candidates/${candidate.id}`} className="hover:text-primary-600">
+                        {candidate.first_name} {candidate.last_name}
+                      </Link>
+                    </h3>
+                    <p className="text-sm text-gray-500">{candidate.email}</p>
+                    
+                    {candidate.location && (
+                      <div className="mt-2 flex items-center text-sm text-gray-500">
+                        <MapPin className="h-4 w-4 mr-1" />
+                        {candidate.location}
+                      </div>
+                    )}
+ 
+                    {candidate.experience_level && (
+                      <div className="mt-2">
+                        <span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getExperienceColor(candidate.experience_level)}`}>
+                          {candidate.experience_level} level
+                        </span>
+                      </div>
+                    )}
+ 
+                    {candidate.bio && (
+                      <div className="mt-3">
+                        <p className="text-sm text-gray-600 line-clamp-3">
+                          {candidate.bio}
+                        </p>
+                      </div>
+                    )}
+ 
+                    {candidate.skills && candidate.skills.length > 0 && (
+                      <div className="mt-3">
+                        <div className="flex flex-wrap gap-1">
+                          {candidate.skills.slice(0, 4).map((skill, index) => (
+                            <span
+                              key={index}
+                              className="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-gray-100 text-gray-800"
+                            >
+                              {skill}
+                            </span>
+                          ))}
+                          {candidate.skills.length > 4 && (
+                            <span className="text-xs text-gray-500">
+                              +{candidate.skills.length - 4} more
+                            </span>
+                          )}
+                        </div>
+                      </div>
+                    )}
+ 
+                    {candidate.salary_expectation && (
+                      <div className="mt-3 flex items-center text-sm text-gray-500">
+                        <Star className="h-4 w-4 mr-1" />
+                        Expected: ${candidate.salary_expectation?.toLocaleString()}
+                      </div>
+                    )}
+                  </div>
+                </div>
+              </div>
+            </div>
+          ))
+        ) : (
+          <div className="col-span-full text-center py-12">
+            <User className="mx-auto h-12 w-12 text-gray-400" />
+            <h3 className="mt-2 text-sm font-medium text-gray-900">No candidates found</h3>
+            <p className="mt-1 text-sm text-gray-500">
+              Try adjusting your search criteria
+            </p>
+          </div>
+        )}
+      </div>
+ 
+      {/* Pagination */}
+      {data?.pagination && data.pagination.pages > 1 && (
+        <div className="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6">
+          <div className="flex-1 flex justify-between sm:hidden">
+            <button className="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
+              Previous
+            </button>
+            <button className="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
+              Next
+            </button>
+          </div>
+          <div className="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
+            <div>
+              <p className="text-sm text-gray-700">
+                Showing{' '}
+                <span className="font-medium">
+                  {((data.pagination.page - 1) * data.pagination.limit) + 1}
+                </span>{' '}
+                to{' '}
+                <span className="font-medium">
+                  {Math.min(data.pagination.page * data.pagination.limit, data.pagination.total)}
+                </span>{' '}
+                of{' '}
+                <span className="font-medium">{data.pagination.total}</span>{' '}
+                results
+              </p>
+            </div>
+          </div>
+        </div>
+      )}
+    </div>
+  );
+};
+ 
+export default Candidates;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/pages/CreateJob.js.html b/frontend/coverage/lcov-report/src/pages/CreateJob.js.html new file mode 100644 index 0000000..e4b15fc --- /dev/null +++ b/frontend/coverage/lcov-report/src/pages/CreateJob.js.html @@ -0,0 +1,1471 @@ + + + + + + Code coverage report for src/pages/CreateJob.js + + + + + + + + + +
+
+

All files / src/pages CreateJob.js

+
+ +
+ 1.96% + Statements + 1/51 +
+ + +
+ 0% + Branches + 0/22 +
+ + +
+ 0% + Functions + 0/37 +
+ + +
+ 2.12% + Lines + 1/47 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { useMutation } from 'react-query';
+import axios from 'axios';
+import toast from 'react-hot-toast';
+import { ArrowLeft, Save, Plus, X } from 'lucide-react';
+ 
+const CreateJob = () => {
+  const navigate = useNavigate();
+  const [formData, setFormData] = useState({
+    title: '',
+    description: '',
+    requirements: [''],
+    responsibilities: [''],
+    location: '',
+    employmentType: 'full-time',
+    salaryMin: '',
+    salaryMax: '',
+    currency: 'USD',
+    remoteAllowed: false,
+    experienceLevel: '',
+    skillsRequired: [''],
+    benefits: [''],
+    applicationDeadline: ''
+  });
+ 
+  const createJobMutation = useMutation(async (jobData) => {
+    const response = await axios.post('/api/jobs', jobData);
+    return response.data;
+  }, {
+    onSuccess: () => {
+      toast.success('Job posted successfully!');
+      navigate('/jobs');
+    },
+    onError: (error) => {
+      toast.error(error.response?.data?.error || 'Failed to create job');
+    }
+  });
+ 
+  const handleChange = (e) => {
+    const { name, value, type, checked } = e.target;
+    setFormData(prev => ({
+      ...prev,
+      [name]: type === 'checkbox' ? checked : value
+    }));
+  };
+ 
+  const handleArrayChange = (field, index, value) => {
+    setFormData(prev => ({
+      ...prev,
+      [field]: prev[field].map((item, i) => i === index ? value : item)
+    }));
+  };
+ 
+  const addArrayItem = (field) => {
+    setFormData(prev => ({
+      ...prev,
+      [field]: [...prev[field], '']
+    }));
+  };
+ 
+  const removeArrayItem = (field, index) => {
+    setFormData(prev => ({
+      ...prev,
+      [field]: prev[field].filter((_, i) => i !== index)
+    }));
+  };
+ 
+  const handleSubmit = (e) => {
+    e.preventDefault();
+    
+    // Filter out empty array items
+    const cleanedData = {
+      ...formData,
+      requirements: formData.requirements.filter(req => req.trim()),
+      responsibilities: formData.responsibilities.filter(resp => resp.trim()),
+      skillsRequired: formData.skillsRequired.filter(skill => skill.trim()),
+      benefits: formData.benefits.filter(benefit => benefit.trim()),
+      salaryMin: formData.salaryMin ? parseInt(formData.salaryMin) : undefined,
+      salaryMax: formData.salaryMax ? parseInt(formData.salaryMax) : undefined,
+      applicationDeadline: formData.applicationDeadline || undefined
+    };
+ 
+    createJobMutation.mutate(cleanedData);
+  };
+ 
+  return (
+    <div className="space-y-6">
+      <div className="flex items-center">
+        <button
+          onClick={() => navigate('/jobs')}
+          className="mr-4 p-2 text-gray-400 hover:text-gray-600"
+        >
+          <ArrowLeft className="h-5 w-5" />
+        </button>
+        <div>
+          <h1 className="text-2xl font-bold text-gray-900">Post a New Job</h1>
+          <p className="mt-1 text-sm text-gray-500">
+            Create a job posting to attract qualified candidates
+          </p>
+        </div>
+      </div>
+ 
+      <form onSubmit={handleSubmit} className="space-y-6">
+        {/* Basic Information */}
+        <div className="bg-white shadow rounded-lg">
+          <div className="px-4 py-5 sm:p-6">
+            <h3 className="text-lg font-medium text-gray-900 mb-4">Basic Information</h3>
+            <div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
+              <div className="sm:col-span-2">
+                <label htmlFor="title" className="block text-sm font-medium text-gray-700">
+                  Job Title *
+                </label>
+                <input
+                  type="text"
+                  name="title"
+                  id="title"
+                  required
+                  className="mt-1 input"
+                  value={formData.title}
+                  onChange={handleChange}
+                  placeholder="e.g., Senior Software Engineer"
+                />
+              </div>
+ 
+              <div className="sm:col-span-2">
+                <label htmlFor="description" className="block text-sm font-medium text-gray-700">
+                  Job Description *
+                </label>
+                <textarea
+                  name="description"
+                  id="description"
+                  rows={4}
+                  required
+                  className="mt-1 input"
+                  value={formData.description}
+                  onChange={handleChange}
+                  placeholder="Describe the role, company culture, and what makes this opportunity special..."
+                />
+              </div>
+ 
+              <div>
+                <label htmlFor="location" className="block text-sm font-medium text-gray-700">
+                  Location *
+                </label>
+                <input
+                  type="text"
+                  name="location"
+                  id="location"
+                  required
+                  className="mt-1 input"
+                  value={formData.location}
+                  onChange={handleChange}
+                  placeholder="e.g., San Francisco, CA"
+                />
+              </div>
+ 
+              <div>
+                <label htmlFor="employmentType" className="block text-sm font-medium text-gray-700">
+                  Employment Type *
+                </label>
+                <select
+                  name="employmentType"
+                  id="employmentType"
+                  required
+                  className="mt-1 input"
+                  value={formData.employmentType}
+                  onChange={handleChange}
+                >
+                  <option value="full-time">Full-time</option>
+                  <option value="part-time">Part-time</option>
+                  <option value="contract">Contract</option>
+                  <option value="internship">Internship</option>
+                </select>
+              </div>
+            </div>
+          </div>
+        </div>
+ 
+        {/* Requirements and Responsibilities */}
+        <div className="bg-white shadow rounded-lg">
+          <div className="px-4 py-5 sm:p-6">
+            <h3 className="text-lg font-medium text-gray-900 mb-4">Requirements & Responsibilities</h3>
+            
+            <div className="space-y-6">
+              <div>
+                <label className="block text-sm font-medium text-gray-700 mb-2">
+                  Requirements
+                </label>
+                {formData.requirements.map((req, index) => (
+                  <div key={index} className="flex items-center mb-2">
+                    <input
+                      type="text"
+                      className="input flex-1"
+                      value={req}
+                      onChange={(e) => handleArrayChange('requirements', index, e.target.value)}
+                      placeholder="e.g., 5+ years of experience in React"
+                    />
+                    {formData.requirements.length > 1 && (
+                      <button
+                        type="button"
+                        onClick={() => removeArrayItem('requirements', index)}
+                        className="ml-2 p-2 text-red-600 hover:text-red-800"
+                      >
+                        <X className="h-4 w-4" />
+                      </button>
+                    )}
+                  </div>
+                ))}
+                <button
+                  type="button"
+                  onClick={() => addArrayItem('requirements')}
+                  className="btn btn-secondary text-sm"
+                >
+                  <Plus className="h-4 w-4 mr-1" />
+                  Add Requirement
+                </button>
+              </div>
+ 
+              <div>
+                <label className="block text-sm font-medium text-gray-700 mb-2">
+                  Responsibilities
+                </label>
+                {formData.responsibilities.map((resp, index) => (
+                  <div key={index} className="flex items-center mb-2">
+                    <input
+                      type="text"
+                      className="input flex-1"
+                      value={resp}
+                      onChange={(e) => handleArrayChange('responsibilities', index, e.target.value)}
+                      placeholder="e.g., Develop and maintain web applications"
+                    />
+                    {formData.responsibilities.length > 1 && (
+                      <button
+                        type="button"
+                        onClick={() => removeArrayItem('responsibilities', index)}
+                        className="ml-2 p-2 text-red-600 hover:text-red-800"
+                      >
+                        <X className="h-4 w-4" />
+                      </button>
+                    )}
+                  </div>
+                ))}
+                <button
+                  type="button"
+                  onClick={() => addArrayItem('responsibilities')}
+                  className="btn btn-secondary text-sm"
+                >
+                  <Plus className="h-4 w-4 mr-1" />
+                  Add Responsibility
+                </button>
+              </div>
+            </div>
+          </div>
+        </div>
+ 
+        {/* Compensation */}
+        <div className="bg-white shadow rounded-lg">
+          <div className="px-4 py-5 sm:p-6">
+            <h3 className="text-lg font-medium text-gray-900 mb-4">Compensation</h3>
+            <div className="grid grid-cols-1 gap-6 sm:grid-cols-3">
+              <div>
+                <label htmlFor="salaryMin" className="block text-sm font-medium text-gray-700">
+                  Minimum Salary
+                </label>
+                <input
+                  type="number"
+                  name="salaryMin"
+                  id="salaryMin"
+                  className="mt-1 input"
+                  value={formData.salaryMin}
+                  onChange={handleChange}
+                  placeholder="e.g., 80000"
+                />
+              </div>
+ 
+              <div>
+                <label htmlFor="salaryMax" className="block text-sm font-medium text-gray-700">
+                  Maximum Salary
+                </label>
+                <input
+                  type="number"
+                  name="salaryMax"
+                  id="salaryMax"
+                  className="mt-1 input"
+                  value={formData.salaryMax}
+                  onChange={handleChange}
+                  placeholder="e.g., 120000"
+                />
+              </div>
+ 
+              <div>
+                <label htmlFor="currency" className="block text-sm font-medium text-gray-700">
+                  Currency
+                </label>
+                <select
+                  name="currency"
+                  id="currency"
+                  className="mt-1 input"
+                  value={formData.currency}
+                  onChange={handleChange}
+                >
+                  <option value="USD">USD</option>
+                  <option value="EUR">EUR</option>
+                  <option value="GBP">GBP</option>
+                  <option value="CAD">CAD</option>
+                </select>
+              </div>
+            </div>
+          </div>
+        </div>
+ 
+        {/* Additional Details */}
+        <div className="bg-white shadow rounded-lg">
+          <div className="px-4 py-5 sm:p-6">
+            <h3 className="text-lg font-medium text-gray-900 mb-4">Additional Details</h3>
+            <div className="space-y-6">
+              <div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
+                <div>
+                  <label htmlFor="experienceLevel" className="block text-sm font-medium text-gray-700">
+                    Experience Level
+                  </label>
+                  <select
+                    name="experienceLevel"
+                    id="experienceLevel"
+                    className="mt-1 input"
+                    value={formData.experienceLevel}
+                    onChange={handleChange}
+                  >
+                    <option value="">Select level</option>
+                    <option value="entry">Entry Level</option>
+                    <option value="mid">Mid Level</option>
+                    <option value="senior">Senior Level</option>
+                    <option value="lead">Lead</option>
+                    <option value="executive">Executive</option>
+                  </select>
+                </div>
+ 
+                <div>
+                  <label htmlFor="applicationDeadline" className="block text-sm font-medium text-gray-700">
+                    Application Deadline
+                  </label>
+                  <input
+                    type="date"
+                    name="applicationDeadline"
+                    id="applicationDeadline"
+                    className="mt-1 input"
+                    value={formData.applicationDeadline}
+                    onChange={handleChange}
+                  />
+                </div>
+              </div>
+ 
+              <div className="flex items-center">
+                <input
+                  type="checkbox"
+                  name="remoteAllowed"
+                  id="remoteAllowed"
+                  className="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded"
+                  checked={formData.remoteAllowed}
+                  onChange={handleChange}
+                />
+                <label htmlFor="remoteAllowed" className="ml-2 block text-sm text-gray-900">
+                  Remote work allowed
+                </label>
+              </div>
+ 
+              <div>
+                <label className="block text-sm font-medium text-gray-700 mb-2">
+                  Required Skills
+                </label>
+                {formData.skillsRequired.map((skill, index) => (
+                  <div key={index} className="flex items-center mb-2">
+                    <input
+                      type="text"
+                      className="input flex-1"
+                      value={skill}
+                      onChange={(e) => handleArrayChange('skillsRequired', index, e.target.value)}
+                      placeholder="e.g., JavaScript, React, Node.js"
+                    />
+                    {formData.skillsRequired.length > 1 && (
+                      <button
+                        type="button"
+                        onClick={() => removeArrayItem('skillsRequired', index)}
+                        className="ml-2 p-2 text-red-600 hover:text-red-800"
+                      >
+                        <X className="h-4 w-4" />
+                      </button>
+                    )}
+                  </div>
+                ))}
+                <button
+                  type="button"
+                  onClick={() => addArrayItem('skillsRequired')}
+                  className="btn btn-secondary text-sm"
+                >
+                  <Plus className="h-4 w-4 mr-1" />
+                  Add Skill
+                </button>
+              </div>
+ 
+              <div>
+                <label className="block text-sm font-medium text-gray-700 mb-2">
+                  Benefits
+                </label>
+                {formData.benefits.map((benefit, index) => (
+                  <div key={index} className="flex items-center mb-2">
+                    <input
+                      type="text"
+                      className="input flex-1"
+                      value={benefit}
+                      onChange={(e) => handleArrayChange('benefits', index, e.target.value)}
+                      placeholder="e.g., Health insurance, 401k, Flexible hours"
+                    />
+                    {formData.benefits.length > 1 && (
+                      <button
+                        type="button"
+                        onClick={() => removeArrayItem('benefits', index)}
+                        className="ml-2 p-2 text-red-600 hover:text-red-800"
+                      >
+                        <X className="h-4 w-4" />
+                      </button>
+                    )}
+                  </div>
+                ))}
+                <button
+                  type="button"
+                  onClick={() => addArrayItem('benefits')}
+                  className="btn btn-secondary text-sm"
+                >
+                  <Plus className="h-4 w-4 mr-1" />
+                  Add Benefit
+                </button>
+              </div>
+            </div>
+          </div>
+        </div>
+ 
+        {/* Submit Button */}
+        <div className="flex justify-end space-x-3">
+          <button
+            type="button"
+            onClick={() => navigate('/jobs')}
+            className="btn btn-secondary"
+          >
+            Cancel
+          </button>
+          <button
+            type="submit"
+            disabled={createJobMutation.isLoading}
+            className="btn btn-primary disabled:opacity-50"
+          >
+            <Save className="h-4 w-4 mr-2" />
+            {createJobMutation.isLoading ? 'Creating...' : 'Post Job'}
+          </button>
+        </div>
+      </form>
+    </div>
+  );
+};
+ 
+export default CreateJob;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/pages/Dashboard.js.html b/frontend/coverage/lcov-report/src/pages/Dashboard.js.html new file mode 100644 index 0000000..ff9ed87 --- /dev/null +++ b/frontend/coverage/lcov-report/src/pages/Dashboard.js.html @@ -0,0 +1,934 @@ + + + + + + Code coverage report for src/pages/Dashboard.js + + + + + + + + + +
+
+

All files / src/pages Dashboard.js

+
+ +
+ 3.7% + Statements + 1/27 +
+ + +
+ 0% + Branches + 0/47 +
+ + +
+ 0% + Functions + 0/8 +
+ + +
+ 4% + Lines + 1/25 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import React from 'react';
+import { Link } from 'react-router-dom';
+import { useAuth } from '../contexts/AuthContext';
+import { useQuery } from 'react-query';
+import axios from 'axios';
+import {
+  Briefcase,
+  Users,
+  FileText,
+  Building,
+  TrendingUp,
+  Clock
+} from 'lucide-react';
+ 
+const Dashboard = () => {
+  const { user } = useAuth();
+ 
+  const { data: stats, isLoading } = useQuery('dashboard-stats', async () => {
+    const [jobsRes, applicationsRes, candidatesRes, employersRes] = await Promise.all([
+      axios.get('/api/jobs?limit=1'),
+      axios.get('/api/applications?limit=1'),
+      user?.role === 'candidate' ? Promise.resolve({ data: { applications: { pagination: { total: 0 } } } }) : axios.get('/api/applications?limit=1'),
+      user?.role === 'employer' || user?.role === 'admin' || user?.role === 'recruiter' ? axios.get('/api/employers?limit=1') : Promise.resolve({ data: [] })
+    ]);
+ 
+    return {
+      totalJobs: jobsRes.data.pagination?.total || 0,
+      totalApplications: applicationsRes.data.pagination?.total || 0,
+      totalCandidates: candidatesRes.data.pagination?.total || 0,
+      totalEmployers: employersRes.data.length || 0
+    };
+  });
+ 
+  const { data: recentJobs } = useQuery('recent-jobs', async () => {
+    const response = await axios.get('/api/jobs?limit=5');
+    return response.data.jobs;
+  });
+ 
+  const { data: recentApplications } = useQuery('recent-applications', async () => {
+    if (user?.role === 'candidate') {
+      const response = await axios.get('/api/applications?limit=5');
+      return response.data.applications;
+    }
+    return [];
+  });
+ 
+  const statsCards = [
+    {
+      name: 'Total Jobs',
+      value: stats?.totalJobs || 0,
+      icon: Briefcase,
+      color: 'bg-blue-500'
+    },
+    {
+      name: 'Applications',
+      value: stats?.totalApplications || 0,
+      icon: FileText,
+      color: 'bg-green-500'
+    },
+    {
+      name: 'Candidates',
+      value: stats?.totalCandidates || 0,
+      icon: Users,
+      color: 'bg-purple-500'
+    },
+    {
+      name: 'Employers',
+      value: stats?.totalEmployers || 0,
+      icon: Building,
+      color: 'bg-orange-500'
+    }
+  ];
+ 
+  const getGreeting = () => {
+    const hour = new Date().getHours();
+    if (hour < 12) return 'Good morning';
+    if (hour < 18) return 'Good afternoon';
+    return 'Good evening';
+  };
+ 
+  if (isLoading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div
+          role="status"
+          aria-label="Loading dashboard"
+          className="animate-spin rounded-full h-32 w-32 border-b-2 border-primary-600"
+        ></div>
+      </div>
+    );
+  }
+ 
+  return (
+    <div className="space-y-6">
+      <div>
+        <h1 className="text-2xl font-bold text-gray-900">
+          {getGreeting()}, {user?.firstName}!
+        </h1>
+        <p className="mt-1 text-sm text-gray-500">
+          Welcome to your MerchantsOfHope-SupplyANdDemandPortal dashboard
+        </p>
+      </div>
+ 
+      {/* Stats Cards */}
+      <div className="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-4">
+        {statsCards.map((card) => (
+          <div key={card.name} className="bg-white overflow-hidden shadow rounded-lg">
+            <div className="p-5">
+              <div className="flex items-center">
+                <div className="flex-shrink-0">
+                  <div className={`p-3 rounded-md ${card.color}`}>
+                    <card.icon className="h-6 w-6 text-white" />
+                  </div>
+                </div>
+                <div className="ml-5 w-0 flex-1">
+                  <dl>
+                    <dt className="text-sm font-medium text-gray-500 truncate">
+                      {card.name}
+                    </dt>
+                    <dd className="text-lg font-medium text-gray-900">
+                      {card.value}
+                    </dd>
+                  </dl>
+                </div>
+              </div>
+            </div>
+          </div>
+        ))}
+      </div>
+ 
+      <div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
+        {/* Recent Jobs */}
+        <div className="bg-white shadow rounded-lg">
+          <div className="px-4 py-5 sm:p-6">
+            <h3 className="text-lg leading-6 font-medium text-gray-900">
+              Recent Job Postings
+            </h3>
+            <div className="mt-5">
+              {recentJobs?.length > 0 ? (
+                <div className="space-y-3">
+                  {recentJobs.map((job) => (
+                    <div key={job.id} className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
+                      <div className="flex-1 min-w-0">
+                        <p className="text-sm font-medium text-gray-900 truncate">
+                          {job.title}
+                        </p>
+                        <p className="text-sm text-gray-500">
+                          {job.company_name}
+                        </p>
+                      </div>
+                      <div className="flex items-center text-sm text-gray-500">
+                        <Clock className="h-4 w-4 mr-1" />
+                        {new Date(job.created_at).toLocaleDateString()}
+                      </div>
+                    </div>
+                  ))}
+                </div>
+              ) : (
+                <p className="text-sm text-gray-500">No recent jobs found</p>
+              )}
+            </div>
+          </div>
+        </div>
+ 
+        {/* Recent Applications */}
+        <div className="bg-white shadow rounded-lg">
+          <div className="px-4 py-5 sm:p-6">
+            <h3 className="text-lg leading-6 font-medium text-gray-900">
+              Recent Applications
+            </h3>
+            <div className="mt-5">
+              {recentApplications?.length > 0 ? (
+                <div className="space-y-3">
+                  {recentApplications.map((application) => (
+                    <div key={application.id} className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
+                      <div className="flex-1 min-w-0">
+                        <p className="text-sm font-medium text-gray-900 truncate">
+                          {application.job_title}
+                        </p>
+                        <p className="text-sm text-gray-500">
+                          {application.company_name}
+                        </p>
+                      </div>
+                      <div className="flex items-center">
+                        <span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
+                          application.status === 'applied' ? 'bg-blue-100 text-blue-800' :
+                          application.status === 'reviewed' ? 'bg-yellow-100 text-yellow-800' :
+                          application.status === 'shortlisted' ? 'bg-green-100 text-green-800' :
+                          application.status === 'rejected' ? 'bg-red-100 text-red-800' :
+                          'bg-gray-100 text-gray-800'
+                        }`}>
+                          {application.status}
+                        </span>
+                      </div>
+                    </div>
+                  ))}
+                </div>
+              ) : (
+                <p className="text-sm text-gray-500">No recent applications found</p>
+              )}
+            </div>
+          </div>
+        </div>
+      </div>
+ 
+      {/* Quick Actions */}
+      <div className="bg-white shadow rounded-lg">
+        <div className="px-4 py-5 sm:p-6">
+          <h3 className="text-lg leading-6 font-medium text-gray-900">
+            Quick Actions
+          </h3>
+          <div className="mt-5 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
+            {user?.role === 'employer' && (
+              <Link
+                to="/jobs/create"
+                className="relative group bg-white p-6 focus-within:ring-2 focus-within:ring-inset focus-within:ring-primary-500 rounded-lg border border-gray-200 hover:border-gray-300"
+              >
+                <div>
+                  <span className="rounded-lg inline-flex p-3 bg-primary-50 text-primary-700 ring-4 ring-white">
+                    <Briefcase className="h-6 w-6" />
+                  </span>
+                </div>
+                <div className="mt-8">
+                  <h3 className="text-lg font-medium">
+                    <span className="absolute inset-0" />
+                    Post a Job
+                  </h3>
+                  <p className="mt-2 text-sm text-gray-500">
+                    Create a new job posting to attract candidates
+                  </p>
+                </div>
+              </Link>
+            )}
+            
+            {user?.role === 'candidate' && (
+              <a
+                href="/jobs"
+                className="relative group bg-white p-6 focus-within:ring-2 focus-within:ring-inset focus-within:ring-primary-500 rounded-lg border border-gray-200 hover:border-gray-300"
+              >
+                <div>
+                  <span className="rounded-lg inline-flex p-3 bg-primary-50 text-primary-700 ring-4 ring-white">
+                    <TrendingUp className="h-6 w-6" />
+                  </span>
+                </div>
+                <div className="mt-8">
+                  <h3 className="text-lg font-medium">
+                    <span className="absolute inset-0" />
+                    Browse Jobs
+                  </h3>
+                  <p className="mt-2 text-sm text-gray-500">
+                    Find your next career opportunity
+                  </p>
+                </div>
+              </a>
+            )}
+ 
+            <a
+              href="/applications"
+              className="relative group bg-white p-6 focus-within:ring-2 focus-within:ring-inset focus-within:ring-primary-500 rounded-lg border border-gray-200 hover:border-gray-300"
+            >
+              <div>
+                <span className="rounded-lg inline-flex p-3 bg-primary-50 text-primary-700 ring-4 ring-white">
+                  <FileText className="h-6 w-6" />
+                </span>
+              </div>
+              <div className="mt-8">
+                <h3 className="text-lg font-medium">
+                  <span className="absolute inset-0" />
+                  View Applications
+                </h3>
+                <p className="mt-2 text-sm text-gray-500">
+                  Track your application status
+                </p>
+              </div>
+            </a>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};
+ 
+export default Dashboard;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/pages/EmployerDetails.js.html b/frontend/coverage/lcov-report/src/pages/EmployerDetails.js.html new file mode 100644 index 0000000..331b471 --- /dev/null +++ b/frontend/coverage/lcov-report/src/pages/EmployerDetails.js.html @@ -0,0 +1,418 @@ + + + + + + Code coverage report for src/pages/EmployerDetails.js + + + + + + + + + +
+
+

All files / src/pages EmployerDetails.js

+
+ +
+ 10% + Statements + 1/10 +
+ + +
+ 0% + Branches + 0/16 +
+ + +
+ 0% + Functions + 0/2 +
+ + +
+ 10% + Lines + 1/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import React from 'react';
+import { useParams } from 'react-router-dom';
+import { useQuery } from 'react-query';
+import axios from 'axios';
+import { Building, MapPin, Users, Globe, Phone } from 'lucide-react';
+ 
+const EmployerDetails = () => {
+  const { id } = useParams();
+ 
+  const { data: employer, isLoading } = useQuery(['employer', id], async () => {
+    const response = await axios.get(`/api/employers/${id}`);
+    return response.data;
+  });
+ 
+  if (isLoading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-primary-600"></div>
+      </div>
+    );
+  }
+ 
+  if (!employer) {
+    return (
+      <div className="text-center py-12">
+        <h3 className="text-lg font-medium text-gray-900">Employer not found</h3>
+        <p className="mt-1 text-sm text-gray-500">
+          The employer you're looking for doesn't exist.
+        </p>
+      </div>
+    );
+  }
+ 
+  return (
+    <div className="space-y-6">
+      <div className="bg-white shadow rounded-lg">
+        <div className="px-4 py-5 sm:p-6">
+          <div className="flex items-start">
+            <div className="flex-shrink-0">
+              <div className="h-16 w-16 rounded-full bg-primary-100 flex items-center justify-center">
+                <Building className="h-8 w-8 text-primary-600" />
+              </div>
+            </div>
+            <div className="ml-6 flex-1">
+              <h1 className="text-2xl font-bold text-gray-900">
+                {employer.company_name}
+              </h1>
+              <p className="text-lg text-gray-600">{employer.first_name} {employer.last_name}</p>
+              <p className="text-sm text-gray-500">{employer.email}</p>
+              
+              <div className="mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2">
+                {employer.industry && (
+                  <div className="flex items-center text-sm text-gray-500">
+                    <Building className="h-4 w-4 mr-2" />
+                    {employer.industry}
+                  </div>
+                )}
+                
+                {employer.company_size && (
+                  <div className="flex items-center text-sm text-gray-500">
+                    <Users className="h-4 w-4 mr-2" />
+                    {employer.company_size} employees
+                  </div>
+                )}
+                
+                {employer.website && (
+                  <div className="flex items-center text-sm text-gray-500">
+                    <Globe className="h-4 w-4 mr-2" />
+                    <a href={employer.website} target="_blank" rel="noopener noreferrer" className="text-primary-600 hover:text-primary-500">
+                      Website
+                    </a>
+                  </div>
+                )}
+                
+                {employer.phone && (
+                  <div className="flex items-center text-sm text-gray-500">
+                    <Phone className="h-4 w-4 mr-2" />
+                    {employer.phone}
+                  </div>
+                )}
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+ 
+      {employer.description && (
+        <div className="bg-white shadow rounded-lg">
+          <div className="px-4 py-5 sm:p-6">
+            <h2 className="text-lg font-medium text-gray-900 mb-4">About {employer.company_name}</h2>
+            <p className="text-gray-600 whitespace-pre-wrap">{employer.description}</p>
+          </div>
+        </div>
+      )}
+ 
+      {employer.address && (
+        <div className="bg-white shadow rounded-lg">
+          <div className="px-4 py-5 sm:p-6">
+            <h2 className="text-lg font-medium text-gray-900 mb-4">Address</h2>
+            <div className="flex items-start">
+              <MapPin className="h-5 w-5 text-gray-400 mr-3 mt-0.5" />
+              <p className="text-gray-600">{employer.address}</p>
+            </div>
+          </div>
+        </div>
+      )}
+    </div>
+  );
+};
+ 
+export default EmployerDetails;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/pages/Employers.js.html b/frontend/coverage/lcov-report/src/pages/Employers.js.html new file mode 100644 index 0000000..76f9f1a --- /dev/null +++ b/frontend/coverage/lcov-report/src/pages/Employers.js.html @@ -0,0 +1,379 @@ + + + + + + Code coverage report for src/pages/Employers.js + + + + + + + + + +
+
+

All files / src/pages Employers.js

+
+ +
+ 12.5% + Statements + 1/8 +
+ + +
+ 0% + Branches + 0/12 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 12.5% + Lines + 1/8 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import React from 'react';
+import { Link } from 'react-router-dom';
+import { useQuery } from 'react-query';
+import axios from 'axios';
+import { Building, Users, Globe } from 'lucide-react';
+ 
+const Employers = () => {
+  const { data, isLoading } = useQuery('employers', async () => {
+    const response = await axios.get('/api/employers');
+    return response.data;
+  });
+ 
+  if (isLoading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-primary-600"></div>
+      </div>
+    );
+  }
+ 
+  return (
+    <div className="space-y-6">
+      <div>
+        <h1 className="text-2xl font-bold text-gray-900">Employers</h1>
+        <p className="mt-1 text-sm text-gray-500">
+          Browse companies and employers
+        </p>
+      </div>
+ 
+      <div className="grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3">
+        {data?.length > 0 ? (
+          data.map((employer) => (
+            <div key={employer.id} className="bg-white shadow rounded-lg">
+              <div className="px-4 py-5 sm:p-6">
+                <div className="flex items-start">
+                  <div className="flex-shrink-0">
+                    <div className="h-12 w-12 rounded-full bg-primary-100 flex items-center justify-center">
+                      <Building className="h-6 w-6 text-primary-600" />
+                    </div>
+                  </div>
+                  <div className="ml-4 flex-1">
+                    <h3 className="text-lg font-medium text-gray-900">
+                      <Link to={`/employers/${employer.id}`} className="hover:text-primary-600">
+                        {employer.company_name}
+                      </Link>
+                    </h3>
+                    <p className="text-sm text-gray-500">{employer.first_name} {employer.last_name}</p>
+                    
+                    {employer.industry && (
+                      <div className="mt-2 flex items-center text-sm text-gray-500">
+                        <Building className="h-4 w-4 mr-1" />
+                        {employer.industry}
+                      </div>
+                    )}
+                    
+                    {employer.company_size && (
+                      <div className="mt-1 flex items-center text-sm text-gray-500">
+                        <Users className="h-4 w-4 mr-1" />
+                        {employer.company_size} employees
+                      </div>
+                    )}
+                    
+                    {employer.website && (
+                      <div className="mt-1 flex items-center text-sm text-gray-500">
+                        <Globe className="h-4 w-4 mr-1" />
+                        <a href={employer.website} target="_blank" rel="noopener noreferrer" className="text-primary-600 hover:text-primary-500">
+                          Website
+                        </a>
+                      </div>
+                    )}
+                    
+                    {employer.description && (
+                      <div className="mt-3">
+                        <p className="text-sm text-gray-600 line-clamp-3">
+                          {employer.description}
+                        </p>
+                      </div>
+                    )}
+                  </div>
+                </div>
+              </div>
+            </div>
+          ))
+        ) : (
+          <div className="col-span-full text-center py-12">
+            <Building className="mx-auto h-12 w-12 text-gray-400" />
+            <h3 className="mt-2 text-sm font-medium text-gray-900">No employers found</h3>
+            <p className="mt-1 text-sm text-gray-500">
+              No employers have registered yet.
+            </p>
+          </div>
+        )}
+      </div>
+    </div>
+  );
+};
+ 
+export default Employers;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/pages/JobDetails.js.html b/frontend/coverage/lcov-report/src/pages/JobDetails.js.html new file mode 100644 index 0000000..a0876c9 --- /dev/null +++ b/frontend/coverage/lcov-report/src/pages/JobDetails.js.html @@ -0,0 +1,964 @@ + + + + + + Code coverage report for src/pages/JobDetails.js + + + + + + + + + +
+
+

All files / src/pages JobDetails.js

+
+ +
+ 2.7% + Statements + 1/37 +
+ + +
+ 0% + Branches + 0/51 +
+ + +
+ 0% + Functions + 0/9 +
+ + +
+ 2.94% + Lines + 1/34 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import React, { useState } from 'react';
+import { useParams, Link } from 'react-router-dom';
+import { useQuery } from 'react-query';
+import axios from 'axios';
+import { useAuth } from '../contexts/AuthContext';
+import { MapPin, Clock, DollarSign, Briefcase, Users, Calendar } from 'lucide-react';
+import toast from 'react-hot-toast';
+ 
+const JobDetails = () => {
+  const { id } = useParams();
+  const { user } = useAuth();
+  const [applying, setApplying] = useState(false);
+  const [coverLetter, setCoverLetter] = useState('');
+ 
+  const { data: job, isLoading } = useQuery(['job', id], async () => {
+    const response = await axios.get(`/api/jobs/${id}`);
+    return response.data;
+  });
+ 
+  const handleApply = async () => {
+    if (!coverLetter.trim()) {
+      toast.error('Please provide a cover letter');
+      return;
+    }
+ 
+    setApplying(true);
+    try {
+      await axios.post('/api/applications', {
+        jobId: id,
+        coverLetter: coverLetter.trim()
+      });
+      toast.success('Application submitted successfully!');
+      setCoverLetter('');
+    } catch (error) {
+      toast.error(error.response?.data?.error || 'Failed to submit application');
+    } finally {
+      setApplying(false);
+    }
+  };
+ 
+  if (isLoading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-primary-600"></div>
+      </div>
+    );
+  }
+ 
+  if (!job) {
+    return (
+      <div className="text-center py-12">
+        <h3 className="text-lg font-medium text-gray-900">Job not found</h3>
+        <p className="mt-1 text-sm text-gray-500">
+          The job you're looking for doesn't exist or has been removed.
+        </p>
+        <div className="mt-6">
+          <Link to="/jobs" className="btn btn-primary">
+            Browse Jobs
+          </Link>
+        </div>
+      </div>
+    );
+  }
+ 
+  const formatSalary = (min, max, currency = 'USD') => {
+    if (!min && !max) return 'Salary not specified';
+    if (!min) return `Up to ${currency} ${max?.toLocaleString()}`;
+    if (!max) return `From ${currency} ${min?.toLocaleString()}`;
+    return `${currency} ${min?.toLocaleString()} - ${max?.toLocaleString()}`;
+  };
+ 
+  return (
+    <div className="space-y-6">
+      <div className="flex items-center justify-between">
+        <div>
+          <Link to="/jobs" className="text-sm text-primary-600 hover:text-primary-500">
+            ← Back to Jobs
+          </Link>
+          <h1 className="mt-2 text-3xl font-bold text-gray-900">{job.title}</h1>
+          <div className="mt-2 flex items-center text-lg text-gray-600">
+            <Briefcase className="h-5 w-5 mr-2" />
+            {job.company_name}
+          </div>
+        </div>
+        {user?.role === 'candidate' && job.status === 'active' && (
+          <div className="flex space-x-3">
+            <button
+              onClick={handleApply}
+              disabled={applying}
+              className="btn btn-primary disabled:opacity-50"
+            >
+              {applying ? 'Applying...' : 'Apply Now'}
+            </button>
+          </div>
+        )}
+      </div>
+ 
+      <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
+        <div className="lg:col-span-2 space-y-6">
+          {/* Job Description */}
+          <div className="bg-white shadow rounded-lg">
+            <div className="px-4 py-5 sm:p-6">
+              <h2 className="text-lg font-medium text-gray-900 mb-4">Job Description</h2>
+              <div className="prose max-w-none">
+                <p className="text-gray-600 whitespace-pre-wrap">{job.description}</p>
+              </div>
+            </div>
+          </div>
+ 
+          {/* Requirements */}
+          {job.requirements && job.requirements.length > 0 && (
+            <div className="bg-white shadow rounded-lg">
+              <div className="px-4 py-5 sm:p-6">
+                <h2 className="text-lg font-medium text-gray-900 mb-4">Requirements</h2>
+                <ul className="list-disc list-inside space-y-2">
+                  {job.requirements.map((requirement, index) => (
+                    <li key={index} className="text-gray-600">{requirement}</li>
+                  ))}
+                </ul>
+              </div>
+            </div>
+          )}
+ 
+          {/* Responsibilities */}
+          {job.responsibilities && job.responsibilities.length > 0 && (
+            <div className="bg-white shadow rounded-lg">
+              <div className="px-4 py-5 sm:p-6">
+                <h2 className="text-lg font-medium text-gray-900 mb-4">Responsibilities</h2>
+                <ul className="list-disc list-inside space-y-2">
+                  {job.responsibilities.map((responsibility, index) => (
+                    <li key={index} className="text-gray-600">{responsibility}</li>
+                  ))}
+                </ul>
+              </div>
+            </div>
+          )}
+ 
+          {/* Skills Required */}
+          {job.skills_required && job.skills_required.length > 0 && (
+            <div className="bg-white shadow rounded-lg">
+              <div className="px-4 py-5 sm:p-6">
+                <h2 className="text-lg font-medium text-gray-900 mb-4">Required Skills</h2>
+                <div className="flex flex-wrap gap-2">
+                  {job.skills_required.map((skill, index) => (
+                    <span
+                      key={index}
+                      className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-primary-100 text-primary-800"
+                    >
+                      {skill}
+                    </span>
+                  ))}
+                </div>
+              </div>
+            </div>
+          )}
+ 
+          {/* Benefits */}
+          {job.benefits && job.benefits.length > 0 && (
+            <div className="bg-white shadow rounded-lg">
+              <div className="px-4 py-5 sm:p-6">
+                <h2 className="text-lg font-medium text-gray-900 mb-4">Benefits</h2>
+                <ul className="list-disc list-inside space-y-2">
+                  {job.benefits.map((benefit, index) => (
+                    <li key={index} className="text-gray-600">{benefit}</li>
+                  ))}
+                </ul>
+              </div>
+            </div>
+          )}
+        </div>
+ 
+        {/* Sidebar */}
+        <div className="space-y-6">
+          {/* Job Details */}
+          <div className="bg-white shadow rounded-lg">
+            <div className="px-4 py-5 sm:p-6">
+              <h3 className="text-lg font-medium text-gray-900 mb-4">Job Details</h3>
+              <div className="space-y-4">
+                <div className="flex items-center">
+                  <MapPin className="h-5 w-5 text-gray-400 mr-3" />
+                  <div>
+                    <p className="text-sm font-medium text-gray-900">{job.location}</p>
+                    {job.remote_allowed && (
+                      <p className="text-sm text-gray-500">Remote work allowed</p>
+                    )}
+                  </div>
+                </div>
+ 
+                <div className="flex items-center">
+                  <Briefcase className="h-5 w-5 text-gray-400 mr-3" />
+                  <div>
+                    <p className="text-sm font-medium text-gray-900 capitalize">
+                      {job.employment_type?.replace('-', ' ')}
+                    </p>
+                  </div>
+                </div>
+ 
+                <div className="flex items-center">
+                  <DollarSign className="h-5 w-5 text-gray-400 mr-3" />
+                  <div>
+                    <p className="text-sm font-medium text-gray-900">
+                      {formatSalary(job.salary_min, job.salary_max, job.currency)}
+                    </p>
+                  </div>
+                </div>
+ 
+                {job.experience_level && (
+                  <div className="flex items-center">
+                    <Users className="h-5 w-5 text-gray-400 mr-3" />
+                    <div>
+                      <p className="text-sm font-medium text-gray-900 capitalize">
+                        {job.experience_level} level
+                      </p>
+                    </div>
+                  </div>
+                )}
+ 
+                <div className="flex items-center">
+                  <Clock className="h-5 w-5 text-gray-400 mr-3" />
+                  <div>
+                    <p className="text-sm font-medium text-gray-900">
+                      Posted {new Date(job.created_at).toLocaleDateString()}
+                    </p>
+                  </div>
+                </div>
+ 
+                {job.application_deadline && (
+                  <div className="flex items-center">
+                    <Calendar className="h-5 w-5 text-gray-400 mr-3" />
+                    <div>
+                      <p className="text-sm font-medium text-gray-900">
+                        Apply by {new Date(job.application_deadline).toLocaleDateString()}
+                      </p>
+                    </div>
+                  </div>
+                )}
+              </div>
+            </div>
+          </div>
+ 
+          {/* Company Info */}
+          <div className="bg-white shadow rounded-lg">
+            <div className="px-4 py-5 sm:p-6">
+              <h3 className="text-lg font-medium text-gray-900 mb-4">Company</h3>
+              <div className="space-y-2">
+                <p className="text-sm font-medium text-gray-900">{job.company_name}</p>
+                {job.industry && (
+                  <p className="text-sm text-gray-500">{job.industry}</p>
+                )}
+                {job.company_size && (
+                  <p className="text-sm text-gray-500">{job.company_size} employees</p>
+                )}
+              </div>
+            </div>
+          </div>
+ 
+          {/* Apply Section for Candidates */}
+          {user?.role === 'candidate' && job.status === 'active' && (
+            <div className="bg-white shadow rounded-lg">
+              <div className="px-4 py-5 sm:p-6">
+                <h3 className="text-lg font-medium text-gray-900 mb-4">Apply for this job</h3>
+                <div className="space-y-4">
+                  <div>
+                    <label htmlFor="coverLetter" className="block text-sm font-medium text-gray-700">
+                      Cover Letter
+                    </label>
+                    <textarea
+                      id="coverLetter"
+                      rows={4}
+                      className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
+                      placeholder="Tell us why you're interested in this position..."
+                      value={coverLetter}
+                      onChange={(e) => setCoverLetter(e.target.value)}
+                    />
+                  </div>
+                  <button
+                    onClick={handleApply}
+                    disabled={applying || !coverLetter.trim()}
+                    className="w-full btn btn-primary disabled:opacity-50"
+                  >
+                    {applying ? 'Applying...' : 'Submit Application'}
+                  </button>
+                </div>
+              </div>
+            </div>
+          )}
+        </div>
+      </div>
+    </div>
+  );
+};
+ 
+export default JobDetails;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/pages/Jobs.js.html b/frontend/coverage/lcov-report/src/pages/Jobs.js.html new file mode 100644 index 0000000..1332f39 --- /dev/null +++ b/frontend/coverage/lcov-report/src/pages/Jobs.js.html @@ -0,0 +1,1036 @@ + + + + + + Code coverage report for src/pages/Jobs.js + + + + + + + + + +
+
+

All files / src/pages Jobs.js

+
+ +
+ 56.75% + Statements + 21/37 +
+ + +
+ 47.22% + Branches + 17/36 +
+ + +
+ 50% + Functions + 5/10 +
+ + +
+ 63.63% + Lines + 21/33 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318  +  +  +  +  +  +  +2x +3x +3x +  +  +  +  +  +  +  +  +  +  +  +  +3x +  +  +  +  +  +  +  +  +  +3x +  +  +  +  +  +  +3x +  +  +  +  +3x +1x +1x +1x +1x +  +  +3x +1x +1x +  +  +  +  +  +  +3x +1x +  +  +  +  +  +  +  +  +  +  +2x +1x +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import React, { useState } from 'react';
+import { Link } from 'react-router-dom';
+import { useQuery } from 'react-query';
+import axios from 'axios';
+import { useAuth } from '../contexts/AuthContext';
+import { Search, MapPin, Clock, DollarSign, Briefcase, Plus } from 'lucide-react';
+ 
+const Jobs = () => {
+  const { user } = useAuth();
+  const [filters, setFilters] = useState({
+    search: '',
+    location: '',
+    employmentType: '',
+    experienceLevel: ''
+  });
+ 
+  const {
+    data,
+    isLoading,
+    isError,
+    error,
+    refetch
+  } = useQuery(['jobs', filters], async () => {
+    const params = new URLSearchParams();
+    Object.entries(filters).forEach(([key, value]) => {
+      if (value) params.append(key, value);
+    });
+    
+    const response = await axios.get(`/api/jobs?${params.toString()}`);
+    return response.data;
+  });
+ 
+  const handleFilterChange = (e) => {
+    setFilters({
+      ...filters,
+      [e.target.name]: e.target.value
+    });
+  };
+ 
+  const handleSearch = (e) => {
+    e.preventDefault();
+    refetch();
+  };
+ 
+  const formatSalary = (min, max, currency = 'USD') => {
+    Iif (!min && !max) return 'Salary not specified';
+    Iif (!min) return `Up to ${currency} ${max?.toLocaleString()}`;
+    Iif (!max) return `From ${currency} ${min?.toLocaleString()}`;
+    return `${currency} ${min?.toLocaleString()} - ${max?.toLocaleString()}`;
+  };
+ 
+  const getStatusColor = (status) => {
+    switch (status) {
+      case 'active': return 'bg-green-100 text-green-800';
+      case 'paused': return 'bg-yellow-100 text-yellow-800';
+      case 'closed': return 'bg-red-100 text-red-800';
+      default: return 'bg-gray-100 text-gray-800';
+    }
+  };
+ 
+  if (isLoading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div
+          role="status"
+          aria-label="Loading jobs"
+          className="animate-spin rounded-full h-32 w-32 border-b-2 border-primary-600"
+        ></div>
+      </div>
+    );
+  }
+ 
+  if (isError) {
+    return (
+      <div className="flex flex-col items-center justify-center h-64 text-center space-y-3">
+        <h2 className="text-xl font-semibold text-gray-800">Unable to load jobs</h2>
+        <p className="text-sm text-gray-500">{error?.message || 'Please try again later.'}</p>
+        <button
+          type="button"
+          onClick={() => refetch()}
+          className="btn btn-primary"
+        >
+          Retry
+        </button>
+      </div>
+    );
+  }
+ 
+  return (
+    <div className="space-y-6">
+      <div className="flex items-center justify-between">
+        <div>
+          <h1 className="text-2xl font-bold text-gray-900">Job Postings</h1>
+          <p className="mt-1 text-sm text-gray-500">
+            Find your next career opportunity
+          </p>
+        </div>
+        {(user?.role === 'employer' || user?.role === 'recruiter') && (
+          <Link
+            to="/jobs/create"
+            className="btn btn-primary"
+          >
+            <Plus className="h-4 w-4 mr-2" />
+            Post a Job
+          </Link>
+        )}
+      </div>
+ 
+      {/* Filters */}
+      <div className="bg-white shadow rounded-lg">
+        <div className="px-4 py-5 sm:p-6">
+          <form onSubmit={handleSearch} className="space-y-4">
+            <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
+              <div>
+                <label htmlFor="search" className="block text-sm font-medium text-gray-700">
+                  Search
+                </label>
+                <div className="mt-1 relative">
+                  <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
+                    <Search className="h-5 w-5 text-gray-400" />
+                  </div>
+                  <input
+                    type="text"
+                    name="search"
+                    id="search"
+                    className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
+                    placeholder="Job title or keywords"
+                    value={filters.search}
+                    onChange={handleFilterChange}
+                  />
+                </div>
+              </div>
+ 
+              <div>
+                <label htmlFor="location" className="block text-sm font-medium text-gray-700">
+                  Location
+                </label>
+                <div className="mt-1 relative">
+                  <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
+                    <MapPin className="h-5 w-5 text-gray-400" />
+                  </div>
+                  <input
+                    type="text"
+                    name="location"
+                    id="location"
+                    className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
+                    placeholder="City, state, or remote"
+                    value={filters.location}
+                    onChange={handleFilterChange}
+                  />
+                </div>
+              </div>
+ 
+              <div>
+                <label htmlFor="employmentType" className="block text-sm font-medium text-gray-700">
+                  Employment Type
+                </label>
+                <select
+                  id="employmentType"
+                  name="employmentType"
+                  className="mt-1 block w-full pl-3 pr-10 py-2 text-base border border-gray-300 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm rounded-md"
+                  value={filters.employmentType}
+                  onChange={handleFilterChange}
+                >
+                  <option value="">All Types</option>
+                  <option value="full-time">Full-time</option>
+                  <option value="part-time">Part-time</option>
+                  <option value="contract">Contract</option>
+                  <option value="internship">Internship</option>
+                </select>
+              </div>
+ 
+              <div>
+                <label htmlFor="experienceLevel" className="block text-sm font-medium text-gray-700">
+                  Experience Level
+                </label>
+                <select
+                  id="experienceLevel"
+                  name="experienceLevel"
+                  className="mt-1 block w-full pl-3 pr-10 py-2 text-base border border-gray-300 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm rounded-md"
+                  value={filters.experienceLevel}
+                  onChange={handleFilterChange}
+                >
+                  <option value="">All Levels</option>
+                  <option value="entry">Entry Level</option>
+                  <option value="mid">Mid Level</option>
+                  <option value="senior">Senior Level</option>
+                  <option value="lead">Lead</option>
+                  <option value="executive">Executive</option>
+                </select>
+              </div>
+            </div>
+ 
+            <div className="flex justify-end">
+              <button
+                type="submit"
+                className="btn btn-primary"
+              >
+                Search Jobs
+              </button>
+            </div>
+          </form>
+        </div>
+      </div>
+ 
+      {/* Jobs List */}
+      <div className="space-y-4">
+        {data?.jobs?.length > 0 ? (
+          data.jobs.map((job) => (
+            <div key={job.id} className="bg-white shadow rounded-lg">
+              <div className="px-4 py-5 sm:p-6">
+                <div className="flex items-start justify-between">
+                  <div className="flex-1">
+                    <div className="flex items-center">
+                      <h3 className="text-lg font-medium text-gray-900">
+                        <Link to={`/jobs/${job.id}`} className="hover:text-primary-600">
+                          {job.title}
+                        </Link>
+                      </h3>
+                      <span className={`ml-3 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getStatusColor(job.status)}`}>
+                        {job.status}
+                      </span>
+                    </div>
+                    <div className="mt-1 flex items-center text-sm text-gray-500">
+                      <Briefcase className="h-4 w-4 mr-1" />
+                      {job.company_name}
+                    </div>
+                    <div className="mt-2 flex items-center text-sm text-gray-500 space-x-4">
+                      <div className="flex items-center">
+                        <MapPin className="h-4 w-4 mr-1" />
+                        {job.location}
+                        {job.remote_allowed && <span className="ml-1">(Remote OK)</span>}
+                      </div>
+                      <div className="flex items-center">
+                        <DollarSign className="h-4 w-4 mr-1" />
+                        {formatSalary(job.salary_min, job.salary_max, job.currency)}
+                      </div>
+                      <div className="flex items-center">
+                        <Clock className="h-4 w-4 mr-1" />
+                        {new Date(job.created_at).toLocaleDateString()}
+                      </div>
+                    </div>
+                    <div className="mt-3">
+                      <p className="text-sm text-gray-600 line-clamp-2">
+                        {job.description}
+                      </p>
+                    </div>
+                    {job.skills_required && job.skills_required.length > 0 && (
+                      <div className="mt-3">
+                        <div className="flex flex-wrap gap-2">
+                          {job.skills_required.slice(0, 5).map((skill, index) => (
+                            <span
+                              key={index}
+                              className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-primary-100 text-primary-800"
+                            >
+                              {skill}
+                            </span>
+                          ))}
+                          {job.skills_required.length > 5 && (
+                            <span className="text-xs text-gray-500">
+                              +{job.skills_required.length - 5} more
+                            </span>
+                          )}
+                        </div>
+                      </div>
+                    )}
+                  </div>
+                </div>
+              </div>
+            </div>
+          ))
+        ) : (
+          <div className="text-center py-12">
+            <Briefcase className="mx-auto h-12 w-12 text-gray-400" />
+            <h3 className="mt-2 text-sm font-medium text-gray-900">No jobs found</h3>
+            <p className="mt-1 text-sm text-gray-500">
+              Try adjusting your search criteria
+            </p>
+          </div>
+        )}
+      </div>
+ 
+      {/* Pagination */}
+      {data?.pagination && data.pagination.pages > 1 && (
+        <div className="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6">
+          <div className="flex-1 flex justify-between sm:hidden">
+            <button className="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
+              Previous
+            </button>
+            <button className="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
+              Next
+            </button>
+          </div>
+          <div className="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
+            <div>
+              <p className="text-sm text-gray-700">
+                Showing{' '}
+                <span className="font-medium">
+                  {((data.pagination.page - 1) * data.pagination.limit) + 1}
+                </span>{' '}
+                to{' '}
+                <span className="font-medium">
+                  {Math.min(data.pagination.page * data.pagination.limit, data.pagination.total)}
+                </span>{' '}
+                of{' '}
+                <span className="font-medium">{data.pagination.total}</span>{' '}
+                results
+              </p>
+            </div>
+          </div>
+        </div>
+      )}
+    </div>
+  );
+};
+ 
+export default Jobs;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/pages/Login.js.html b/frontend/coverage/lcov-report/src/pages/Login.js.html new file mode 100644 index 0000000..2cfe612 --- /dev/null +++ b/frontend/coverage/lcov-report/src/pages/Login.js.html @@ -0,0 +1,493 @@ + + + + + + Code coverage report for src/pages/Login.js + + + + + + + + + +
+
+

All files / src/pages Login.js

+
+ +
+ 6.66% + Statements + 1/15 +
+ + +
+ 0% + Branches + 0/8 +
+ + +
+ 0% + Functions + 0/4 +
+ + +
+ 6.66% + Lines + 1/15 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import React, { useState } from 'react';
+import { Link } from 'react-router-dom';
+import { useAuth } from '../contexts/AuthContext';
+import { Eye, EyeOff, Mail, Lock } from 'lucide-react';
+ 
+const Login = () => {
+  const { login } = useAuth();
+  const [formData, setFormData] = useState({
+    email: '',
+    password: ''
+  });
+  const [showPassword, setShowPassword] = useState(false);
+  const [loading, setLoading] = useState(false);
+ 
+  const handleChange = (e) => {
+    setFormData({
+      ...formData,
+      [e.target.name]: e.target.value
+    });
+  };
+ 
+  const handleSubmit = async (e) => {
+    e.preventDefault();
+    setLoading(true);
+    
+    const result = await login(formData.email, formData.password);
+    
+    if (!result.success) {
+      setLoading(false);
+    }
+  };
+ 
+  return (
+    <div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
+      <div className="max-w-md w-full space-y-8">
+        <div>
+          <h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
+            Sign in to your account
+          </h2>
+          <p className="mt-2 text-center text-sm text-gray-600">
+            Or{' '}
+            <Link
+              to="/register"
+              className="font-medium text-primary-600 hover:text-primary-500"
+            >
+              create a new account
+            </Link>
+          </p>
+        </div>
+        <form className="mt-8 space-y-6" onSubmit={handleSubmit}>
+          <div className="rounded-md shadow-sm -space-y-px">
+            <div>
+              <label htmlFor="email" className="sr-only">
+                Email address
+              </label>
+              <div className="relative">
+                <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
+                  <Mail className="h-5 w-5 text-gray-400" />
+                </div>
+                <input
+                  id="email"
+                  name="email"
+                  type="email"
+                  autoComplete="email"
+                  required
+                  className="appearance-none rounded-none relative block w-full px-3 py-2 pl-10 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-primary-500 focus:border-primary-500 focus:z-10 sm:text-sm"
+                  placeholder="Email address"
+                  value={formData.email}
+                  onChange={handleChange}
+                />
+              </div>
+            </div>
+            <div>
+              <label htmlFor="password" className="sr-only">
+                Password
+              </label>
+              <div className="relative">
+                <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
+                  <Lock className="h-5 w-5 text-gray-400" />
+                </div>
+                <input
+                  id="password"
+                  name="password"
+                  type={showPassword ? 'text' : 'password'}
+                  autoComplete="current-password"
+                  required
+                  className="appearance-none rounded-none relative block w-full px-3 py-2 pl-10 pr-10 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-primary-500 focus:border-primary-500 focus:z-10 sm:text-sm"
+                  placeholder="Password"
+                  value={formData.password}
+                  onChange={handleChange}
+                />
+                <div className="absolute inset-y-0 right-0 pr-3 flex items-center">
+                  <button
+                    type="button"
+                    className="text-gray-400 hover:text-gray-500"
+                    onClick={() => setShowPassword(!showPassword)}
+                  >
+                    {showPassword ? (
+                      <EyeOff className="h-5 w-5" />
+                    ) : (
+                      <Eye className="h-5 w-5" />
+                    )}
+                  </button>
+                </div>
+              </div>
+            </div>
+          </div>
+ 
+          <div>
+            <button
+              type="submit"
+              disabled={loading}
+              className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 disabled:opacity-50 disabled:cursor-not-allowed"
+            >
+              {loading ? 'Signing in...' : 'Sign in'}
+            </button>
+          </div>
+ 
+          <div className="text-center">
+            <p className="text-sm text-gray-600">
+              Demo accounts:
+            </p>
+            <div className="mt-2 text-xs text-gray-500 space-y-1">
+              <p>Admin: admin@merchantsofhope.org / admin123</p>
+              <p>Recruiter: recruiter@merchantsofhope.org / recruiter123</p>
+              <p>Employer: employer@techcorp.com / employer123</p>
+              <p>Candidate: candidate@example.com / candidate123</p>
+            </div>
+          </div>
+        </form>
+      </div>
+    </div>
+  );
+};
+ 
+export default Login;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/pages/Profile.js.html b/frontend/coverage/lcov-report/src/pages/Profile.js.html new file mode 100644 index 0000000..940f2a3 --- /dev/null +++ b/frontend/coverage/lcov-report/src/pages/Profile.js.html @@ -0,0 +1,511 @@ + + + + + + Code coverage report for src/pages/Profile.js + + + + + + + + + +
+
+

All files / src/pages Profile.js

+
+ +
+ 6.25% + Statements + 1/16 +
+ + +
+ 0% + Branches + 0/8 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 6.25% + Lines + 1/16 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import React, { useState } from 'react';
+import { useAuth } from '../contexts/AuthContext';
+import { User, Mail, Save } from 'lucide-react';
+import toast from 'react-hot-toast';
+ 
+const Profile = () => {
+  const { user, fetchUser } = useAuth();
+  const [formData, setFormData] = useState({
+    firstName: user?.firstName || '',
+    lastName: user?.lastName || '',
+    email: user?.email || ''
+  });
+  const [loading, setLoading] = useState(false);
+ 
+  const handleChange = (e) => {
+    setFormData({
+      ...formData,
+      [e.target.name]: e.target.value
+    });
+  };
+ 
+  const handleSubmit = async (e) => {
+    e.preventDefault();
+    setLoading(true);
+ 
+    try {
+      await fetch(`/api/users/${user.id}`, {
+        method: 'PUT',
+        headers: {
+          'Content-Type': 'application/json',
+          'Authorization': `Bearer ${localStorage.getItem('token')}`
+        },
+        body: JSON.stringify(formData)
+      });
+ 
+      await fetchUser();
+      toast.success('Profile updated successfully!');
+    } catch (error) {
+      toast.error('Failed to update profile');
+    } finally {
+      setLoading(false);
+    }
+  };
+ 
+  return (
+    <div className="space-y-6">
+      <div>
+        <h1 className="text-2xl font-bold text-gray-900">Profile</h1>
+        <p className="mt-1 text-sm text-gray-500">
+          Manage your account information
+        </p>
+      </div>
+ 
+      <div className="bg-white shadow rounded-lg">
+        <div className="px-4 py-5 sm:p-6">
+          <form onSubmit={handleSubmit} className="space-y-6">
+            <div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
+              <div>
+                <label htmlFor="firstName" className="block text-sm font-medium text-gray-700">
+                  First Name
+                </label>
+                <div className="mt-1 relative">
+                  <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
+                    <User className="h-5 w-5 text-gray-400" />
+                  </div>
+                  <input
+                    type="text"
+                    name="firstName"
+                    id="firstName"
+                    required
+                    className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
+                    value={formData.firstName}
+                    onChange={handleChange}
+                  />
+                </div>
+              </div>
+ 
+              <div>
+                <label htmlFor="lastName" className="block text-sm font-medium text-gray-700">
+                  Last Name
+                </label>
+                <div className="mt-1 relative">
+                  <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
+                    <User className="h-5 w-5 text-gray-400" />
+                  </div>
+                  <input
+                    type="text"
+                    name="lastName"
+                    id="lastName"
+                    required
+                    className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
+                    value={formData.lastName}
+                    onChange={handleChange}
+                  />
+                </div>
+              </div>
+            </div>
+ 
+            <div>
+              <label htmlFor="email" className="block text-sm font-medium text-gray-700">
+                Email Address
+              </label>
+              <div className="mt-1 relative">
+                <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
+                  <Mail className="h-5 w-5 text-gray-400" />
+                </div>
+                <input
+                  type="email"
+                  name="email"
+                  id="email"
+                  required
+                  className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
+                  value={formData.email}
+                  onChange={handleChange}
+                />
+              </div>
+            </div>
+ 
+            <div className="bg-gray-50 px-4 py-3 rounded-md">
+              <p className="text-sm text-gray-600">
+                <strong>Role:</strong> {user?.role}
+              </p>
+            </div>
+ 
+            <div className="flex justify-end">
+              <button
+                type="submit"
+                disabled={loading}
+                className="btn btn-primary disabled:opacity-50"
+              >
+                <Save className="h-4 w-4 mr-2" />
+                {loading ? 'Saving...' : 'Save Changes'}
+              </button>
+            </div>
+          </form>
+        </div>
+      </div>
+    </div>
+  );
+};
+ 
+export default Profile;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/pages/Register.js.html b/frontend/coverage/lcov-report/src/pages/Register.js.html new file mode 100644 index 0000000..a284870 --- /dev/null +++ b/frontend/coverage/lcov-report/src/pages/Register.js.html @@ -0,0 +1,811 @@ + + + + + + Code coverage report for src/pages/Register.js + + + + + + + + + +
+
+

All files / src/pages Register.js

+
+ +
+ 4.34% + Statements + 1/23 +
+ + +
+ 0% + Branches + 0/16 +
+ + +
+ 0% + Functions + 0/5 +
+ + +
+ 4.34% + Lines + 1/23 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import React, { useState } from 'react';
+import { Link } from 'react-router-dom';
+import { useAuth } from '../contexts/AuthContext';
+import { Eye, EyeOff, Mail, Lock, User } from 'lucide-react';
+ 
+const Register = () => {
+  const { register } = useAuth();
+  const [formData, setFormData] = useState({
+    firstName: '',
+    lastName: '',
+    email: '',
+    password: '',
+    confirmPassword: '',
+    role: 'candidate'
+  });
+  const [showPassword, setShowPassword] = useState(false);
+  const [showConfirmPassword, setShowConfirmPassword] = useState(false);
+  const [loading, setLoading] = useState(false);
+ 
+  const handleChange = (e) => {
+    setFormData({
+      ...formData,
+      [e.target.name]: e.target.value
+    });
+  };
+ 
+  const handleSubmit = async (e) => {
+    e.preventDefault();
+    
+    if (formData.password !== formData.confirmPassword) {
+      alert('Passwords do not match');
+      return;
+    }
+ 
+    if (formData.password.length < 6) {
+      alert('Password must be at least 6 characters long');
+      return;
+    }
+ 
+    setLoading(true);
+    
+    const result = await register({
+      firstName: formData.firstName,
+      lastName: formData.lastName,
+      email: formData.email,
+      password: formData.password,
+      role: formData.role
+    });
+    
+    if (!result.success) {
+      setLoading(false);
+    }
+  };
+ 
+  return (
+    <div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
+      <div className="max-w-md w-full space-y-8">
+        <div>
+          <h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
+            Create your account
+          </h2>
+          <p className="mt-2 text-center text-sm text-gray-600">
+            Or{' '}
+            <Link
+              to="/login"
+              className="font-medium text-primary-600 hover:text-primary-500"
+            >
+              sign in to your existing account
+            </Link>
+          </p>
+        </div>
+        <form className="mt-8 space-y-6" onSubmit={handleSubmit}>
+          <div className="space-y-4">
+            <div className="grid grid-cols-2 gap-4">
+              <div>
+                <label htmlFor="firstName" className="block text-sm font-medium text-gray-700">
+                  First Name
+                </label>
+                <div className="mt-1 relative">
+                  <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
+                    <User className="h-5 w-5 text-gray-400" />
+                  </div>
+                  <input
+                    id="firstName"
+                    name="firstName"
+                    type="text"
+                    required
+                    className="appearance-none relative block w-full px-3 py-2 pl-10 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
+                    placeholder="First name"
+                    value={formData.firstName}
+                    onChange={handleChange}
+                  />
+                </div>
+              </div>
+              <div>
+                <label htmlFor="lastName" className="block text-sm font-medium text-gray-700">
+                  Last Name
+                </label>
+                <div className="mt-1 relative">
+                  <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
+                    <User className="h-5 w-5 text-gray-400" />
+                  </div>
+                  <input
+                    id="lastName"
+                    name="lastName"
+                    type="text"
+                    required
+                    className="appearance-none relative block w-full px-3 py-2 pl-10 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
+                    placeholder="Last name"
+                    value={formData.lastName}
+                    onChange={handleChange}
+                  />
+                </div>
+              </div>
+            </div>
+ 
+            <div>
+              <label htmlFor="email" className="block text-sm font-medium text-gray-700">
+                Email Address
+              </label>
+              <div className="mt-1 relative">
+                <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
+                  <Mail className="h-5 w-5 text-gray-400" />
+                </div>
+                <input
+                  id="email"
+                  name="email"
+                  type="email"
+                  autoComplete="email"
+                  required
+                  className="appearance-none relative block w-full px-3 py-2 pl-10 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
+                  placeholder="Email address"
+                  value={formData.email}
+                  onChange={handleChange}
+                />
+              </div>
+            </div>
+ 
+            <div>
+              <label htmlFor="role" className="block text-sm font-medium text-gray-700">
+                Account Type
+              </label>
+              <select
+                id="role"
+                name="role"
+                className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
+                value={formData.role}
+                onChange={handleChange}
+              >
+                <option value="candidate">Candidate</option>
+                <option value="employer">Employer</option>
+                <option value="recruiter">Recruiter</option>
+              </select>
+            </div>
+ 
+            <div>
+              <label htmlFor="password" className="block text-sm font-medium text-gray-700">
+                Password
+              </label>
+              <div className="mt-1 relative">
+                <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
+                  <Lock className="h-5 w-5 text-gray-400" />
+                </div>
+                <input
+                  id="password"
+                  name="password"
+                  type={showPassword ? 'text' : 'password'}
+                  autoComplete="new-password"
+                  required
+                  className="appearance-none relative block w-full px-3 py-2 pl-10 pr-10 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
+                  placeholder="Password"
+                  value={formData.password}
+                  onChange={handleChange}
+                />
+                <div className="absolute inset-y-0 right-0 pr-3 flex items-center">
+                  <button
+                    type="button"
+                    className="text-gray-400 hover:text-gray-500"
+                    onClick={() => setShowPassword(!showPassword)}
+                  >
+                    {showPassword ? (
+                      <EyeOff className="h-5 w-5" />
+                    ) : (
+                      <Eye className="h-5 w-5" />
+                    )}
+                  </button>
+                </div>
+              </div>
+            </div>
+ 
+            <div>
+              <label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-700">
+                Confirm Password
+              </label>
+              <div className="mt-1 relative">
+                <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
+                  <Lock className="h-5 w-5 text-gray-400" />
+                </div>
+                <input
+                  id="confirmPassword"
+                  name="confirmPassword"
+                  type={showConfirmPassword ? 'text' : 'password'}
+                  autoComplete="new-password"
+                  required
+                  className="appearance-none relative block w-full px-3 py-2 pl-10 pr-10 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
+                  placeholder="Confirm password"
+                  value={formData.confirmPassword}
+                  onChange={handleChange}
+                />
+                <div className="absolute inset-y-0 right-0 pr-3 flex items-center">
+                  <button
+                    type="button"
+                    className="text-gray-400 hover:text-gray-500"
+                    onClick={() => setShowConfirmPassword(!showConfirmPassword)}
+                  >
+                    {showConfirmPassword ? (
+                      <EyeOff className="h-5 w-5" />
+                    ) : (
+                      <Eye className="h-5 w-5" />
+                    )}
+                  </button>
+                </div>
+              </div>
+            </div>
+          </div>
+ 
+          <div>
+            <button
+              type="submit"
+              disabled={loading}
+              className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 disabled:opacity-50 disabled:cursor-not-allowed"
+            >
+              {loading ? 'Creating account...' : 'Create account'}
+            </button>
+          </div>
+        </form>
+      </div>
+    </div>
+  );
+};
+ 
+export default Register;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/pages/Resumes.js.html b/frontend/coverage/lcov-report/src/pages/Resumes.js.html new file mode 100644 index 0000000..c666f04 --- /dev/null +++ b/frontend/coverage/lcov-report/src/pages/Resumes.js.html @@ -0,0 +1,820 @@ + + + + + + Code coverage report for src/pages/Resumes.js + + + + + + + + + +
+
+

All files / src/pages Resumes.js

+
+ +
+ 1.56% + Statements + 1/64 +
+ + +
+ 0% + Branches + 0/28 +
+ + +
+ 0% + Functions + 0/11 +
+ + +
+ 1.56% + Lines + 1/64 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import React, { useState } from 'react';
+import { useQuery } from 'react-query';
+import axios from 'axios';
+import { Upload, Download, Trash2, Star, FileText } from 'lucide-react';
+import toast from 'react-hot-toast';
+ 
+const Resumes = () => {
+  const [uploading, setUploading] = useState(false);
+  const [selectedFile, setSelectedFile] = useState(null);
+ 
+  const { data: resumes, isLoading, refetch } = useQuery('resumes', async () => {
+    // This would need to be implemented based on the candidate's ID
+    // For now, return empty array
+    return [];
+  });
+ 
+  const handleFileSelect = (e) => {
+    const file = e.target.files[0];
+    if (file) {
+      if (file.size > 10 * 1024 * 1024) { // 10MB limit
+        toast.error('File size must be less than 10MB');
+        return;
+      }
+      
+      const allowedTypes = [
+        'application/pdf',
+        'application/msword',
+        'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+        'text/plain'
+      ];
+      
+      if (!allowedTypes.includes(file.type)) {
+        toast.error('Only PDF, DOC, DOCX, and TXT files are allowed');
+        return;
+      }
+      
+      setSelectedFile(file);
+    }
+  };
+ 
+  const handleUpload = async () => {
+    if (!selectedFile) {
+      toast.error('Please select a file');
+      return;
+    }
+ 
+    setUploading(true);
+    const formData = new FormData();
+    formData.append('resume', selectedFile);
+    formData.append('isPrimary', resumes?.length === 0 ? 'true' : 'false');
+ 
+    try {
+      await axios.post('/api/resumes/upload', formData, {
+        headers: {
+          'Content-Type': 'multipart/form-data'
+        }
+      });
+      
+      toast.success('Resume uploaded successfully!');
+      setSelectedFile(null);
+      refetch();
+    } catch (error) {
+      toast.error(error.response?.data?.error || 'Failed to upload resume');
+    } finally {
+      setUploading(false);
+    }
+  };
+ 
+  const handleDownload = async (resumeId) => {
+    try {
+      const response = await axios.get(`/api/resumes/${resumeId}/download`, {
+        responseType: 'blob'
+      });
+      
+      const url = window.URL.createObjectURL(new Blob([response.data]));
+      const link = document.createElement('a');
+      link.href = url;
+      link.setAttribute('download', response.headers['content-disposition']?.split('filename=')[1] || 'resume.pdf');
+      document.body.appendChild(link);
+      link.click();
+      link.remove();
+      window.URL.revokeObjectURL(url);
+    } catch (error) {
+      toast.error('Failed to download resume');
+    }
+  };
+ 
+  const handleSetPrimary = async (resumeId) => {
+    try {
+      await axios.put(`/api/resumes/${resumeId}/primary`);
+      toast.success('Primary resume updated!');
+      refetch();
+    } catch (error) {
+      toast.error('Failed to set primary resume');
+    }
+  };
+ 
+  const handleDelete = async (resumeId) => {
+    if (!window.confirm('Are you sure you want to delete this resume?')) {
+      return;
+    }
+ 
+    try {
+      await axios.delete(`/api/resumes/${resumeId}`);
+      toast.success('Resume deleted successfully!');
+      refetch();
+    } catch (error) {
+      toast.error('Failed to delete resume');
+    }
+  };
+ 
+  if (isLoading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-primary-600"></div>
+      </div>
+    );
+  }
+ 
+  return (
+    <div className="space-y-6">
+      <div>
+        <h1 className="text-2xl font-bold text-gray-900">Resumes</h1>
+        <p className="mt-1 text-sm text-gray-500">
+          Manage your resume files
+        </p>
+      </div>
+ 
+      {/* Upload Section */}
+      <div className="bg-white shadow rounded-lg">
+        <div className="px-4 py-5 sm:p-6">
+          <h2 className="text-lg font-medium text-gray-900 mb-4">Upload Resume</h2>
+          <div className="space-y-4">
+            <div>
+              <label htmlFor="resume" className="block text-sm font-medium text-gray-700">
+                Select Resume File
+              </label>
+              <input
+                type="file"
+                id="resume"
+                accept=".pdf,.doc,.docx,.txt"
+                onChange={handleFileSelect}
+                className="mt-1 block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-primary-50 file:text-primary-700 hover:file:bg-primary-100"
+              />
+              <p className="mt-1 text-xs text-gray-500">
+                PDF, DOC, DOCX, or TXT files only. Maximum size: 10MB
+              </p>
+            </div>
+            
+            {selectedFile && (
+              <div className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
+                <div className="flex items-center">
+                  <FileText className="h-5 w-5 text-gray-400 mr-2" />
+                  <span className="text-sm text-gray-900">{selectedFile.name}</span>
+                  <span className="ml-2 text-xs text-gray-500">
+                    ({(selectedFile.size / 1024 / 1024).toFixed(2)} MB)
+                  </span>
+                </div>
+                <button
+                  onClick={handleUpload}
+                  disabled={uploading}
+                  className="btn btn-primary disabled:opacity-50"
+                >
+                  <Upload className="h-4 w-4 mr-2" />
+                  {uploading ? 'Uploading...' : 'Upload'}
+                </button>
+              </div>
+            )}
+          </div>
+        </div>
+      </div>
+ 
+      {/* Resumes List */}
+      <div className="space-y-4">
+        {resumes?.length > 0 ? (
+          resumes.map((resume) => (
+            <div key={resume.id} className="bg-white shadow rounded-lg">
+              <div className="px-4 py-5 sm:p-6">
+                <div className="flex items-center justify-between">
+                  <div className="flex items-center">
+                    <FileText className="h-8 w-8 text-gray-400 mr-3" />
+                    <div>
+                      <h3 className="text-lg font-medium text-gray-900">
+                        {resume.original_name}
+                      </h3>
+                      <div className="flex items-center text-sm text-gray-500">
+                        <span>{(resume.file_size / 1024 / 1024).toFixed(2)} MB</span>
+                        <span className="mx-2">•</span>
+                        <span>Uploaded {new Date(resume.uploaded_at).toLocaleDateString()}</span>
+                        {resume.is_primary && (
+                          <>
+                            <span className="mx-2">•</span>
+                            <span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-primary-100 text-primary-800">
+                              <Star className="h-3 w-3 mr-1" />
+                              Primary
+                            </span>
+                          </>
+                        )}
+                      </div>
+                    </div>
+                  </div>
+                  <div className="flex items-center space-x-2">
+                    <button
+                      onClick={() => handleDownload(resume.id)}
+                      className="btn btn-secondary"
+                    >
+                      <Download className="h-4 w-4 mr-2" />
+                      Download
+                    </button>
+                    {!resume.is_primary && (
+                      <button
+                        onClick={() => handleSetPrimary(resume.id)}
+                        className="btn btn-secondary"
+                      >
+                        <Star className="h-4 w-4 mr-2" />
+                        Set Primary
+                      </button>
+                    )}
+                    <button
+                      onClick={() => handleDelete(resume.id)}
+                      className="btn btn-danger"
+                    >
+                      <Trash2 className="h-4 w-4 mr-2" />
+                      Delete
+                    </button>
+                  </div>
+                </div>
+              </div>
+            </div>
+          ))
+        ) : (
+          <div className="text-center py-12">
+            <FileText className="mx-auto h-12 w-12 text-gray-400" />
+            <h3 className="mt-2 text-sm font-medium text-gray-900">No resumes uploaded</h3>
+            <p className="mt-1 text-sm text-gray-500">
+              Upload your first resume to get started.
+            </p>
+          </div>
+        )}
+      </div>
+    </div>
+  );
+};
+ 
+export default Resumes;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov-report/src/pages/index.html b/frontend/coverage/lcov-report/src/pages/index.html new file mode 100644 index 0000000..2f8a2b3 --- /dev/null +++ b/frontend/coverage/lcov-report/src/pages/index.html @@ -0,0 +1,296 @@ + + + + + + Code coverage report for src/pages + + + + + + + + + +
+
+

All files src/pages

+
+ +
+ 9.34% + Statements + 33/353 +
+ + +
+ 5.34% + Branches + 17/318 +
+ + +
+ 4.62% + Functions + 5/108 +
+ + +
+ 9.73% + Lines + 33/339 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
Applications.js +
+
3.7%1/270%0/230%0/53.7%1/27
CandidateDetails.js +
+
9.09%1/110%0/230%0/39.09%1/11
Candidates.js +
+
3.7%1/270%0/280%0/83.84%1/26
CreateJob.js +
+
1.96%1/510%0/220%0/372.12%1/47
Dashboard.js +
+
3.7%1/270%0/470%0/84%1/25
EmployerDetails.js +
+
10%1/100%0/160%0/210%1/10
Employers.js +
+
12.5%1/80%0/120%0/312.5%1/8
JobDetails.js +
+
2.7%1/370%0/510%0/92.94%1/34
Jobs.js +
+
56.75%21/3747.22%17/3650%5/1063.63%21/33
Login.js +
+
6.66%1/150%0/80%0/46.66%1/15
Profile.js +
+
6.25%1/160%0/80%0/36.25%1/16
Register.js +
+
4.34%1/230%0/160%0/54.34%1/23
Resumes.js +
+
1.56%1/640%0/280%0/111.56%1/64
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/frontend/coverage/lcov.info b/frontend/coverage/lcov.info new file mode 100644 index 0000000..63997c3 --- /dev/null +++ b/frontend/coverage/lcov.info @@ -0,0 +1,1194 @@ +TN: +SF:src/App.js +FN:23,ProtectedRoute +FN:45,AppRoutes +FN:121,App +FNF:3 +FNH:2 +FNDA:0,ProtectedRoute +FNDA:1,AppRoutes +FNDA:1,App +DA:21,1 +DA:24,0 +DA:26,0 +DA:27,0 +DA:34,0 +DA:35,0 +DA:38,0 +DA:39,0 +DA:42,0 +DA:46,1 +DA:48,1 +DA:122,1 +LF:12 +LH:4 +BRDA:23,0,0,0 +BRDA:26,1,0,0 +BRDA:26,1,1,0 +BRDA:34,2,0,0 +BRDA:34,2,1,0 +BRDA:38,3,0,0 +BRDA:38,3,1,0 +BRDA:38,4,0,0 +BRDA:38,4,1,0 +BRDA:50,5,0,1 +BRDA:50,5,1,0 +BRDA:51,6,0,1 +BRDA:51,6,1,0 +BRF:13 +BRH:2 +end_of_record +TN: +SF:src/index.js +FNF:0 +FNH:0 +DA:7,0 +DA:8,0 +LF:2 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/components/Layout.js +FN:17,(anonymous_0) +FN:31,(anonymous_1) +FN:35,(anonymous_2) +FN:43,(anonymous_3) +FN:49,(anonymous_4) +FN:59,(anonymous_5) +FN:89,(anonymous_6) +FN:131,(anonymous_7) +FNF:8 +FNH:4 +FNDA:3,(anonymous_0) +FNDA:18,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:12,(anonymous_5) +FNDA:12,(anonymous_6) +FNDA:0,(anonymous_7) +DA:17,2 +DA:18,3 +DA:19,3 +DA:20,3 +DA:22,3 +DA:31,3 +DA:32,18 +DA:35,3 +DA:36,0 +DA:39,3 +DA:43,0 +DA:49,0 +DA:60,12 +DA:61,12 +DA:90,12 +DA:91,12 +DA:131,0 +LF:17 +LH:13 +BRDA:42,0,0,0 +BRDA:42,0,1,3 +BRDA:66,1,0,3 +BRDA:66,1,1,9 +BRDA:96,2,0,3 +BRDA:96,2,1,9 +BRF:6 +BRH:5 +end_of_record +TN: +SF:src/contexts/AuthContext.js +FN:7,(anonymous_0) +FN:15,(anonymous_1) +FN:19,(anonymous_2) +FN:29,(anonymous_3) +FN:42,(anonymous_4) +FN:60,(anonymous_5) +FN:78,(anonymous_6) +FNF:7 +FNH:5 +FNDA:9,(anonymous_0) +FNDA:9,(anonymous_1) +FNDA:3,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:3,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:1,(anonymous_6) +DA:5,1 +DA:7,1 +DA:8,9 +DA:9,9 +DA:10,0 +DA:12,9 +DA:15,1 +DA:16,9 +DA:17,9 +DA:19,9 +DA:20,3 +DA:21,3 +DA:22,0 +DA:23,0 +DA:25,3 +DA:29,9 +DA:30,0 +DA:31,0 +DA:32,0 +DA:34,0 +DA:35,0 +DA:36,0 +DA:38,0 +DA:42,9 +DA:43,3 +DA:44,3 +DA:45,2 +DA:47,2 +DA:48,2 +DA:49,2 +DA:51,2 +DA:52,2 +DA:54,1 +DA:55,1 +DA:56,1 +DA:60,9 +DA:61,0 +DA:62,0 +DA:63,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:69,0 +DA:70,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:78,9 +DA:79,1 +DA:80,1 +DA:82,0 +DA:84,1 +DA:85,1 +DA:86,1 +DA:87,1 +DA:91,9 +DA:100,9 +LF:57 +LH:35 +BRDA:9,0,0,0 +BRDA:9,0,1,9 +BRDA:21,1,0,0 +BRDA:21,1,1,3 +BRDA:54,2,0,1 +BRDA:54,2,1,0 +BRDA:72,3,0,0 +BRDA:72,3,1,0 +BRF:8 +BRH:3 +end_of_record +TN: +SF:src/lib/configureAxios.js +FNF:0 +FNH:0 +DA:3,0 +DA:5,0 +DA:6,0 +DA:9,0 +LF:4 +LH:0 +BRDA:3,0,0,0 +BRDA:3,0,1,0 +BRDA:5,1,0,0 +BRDA:5,1,1,0 +BRF:4 +BRH:0 +end_of_record +TN: +SF:src/pages/Applications.js +FN:6,(anonymous_0) +FN:7,(anonymous_1) +FN:12,(anonymous_2) +FN:24,(anonymous_3) +FN:56,(anonymous_4) +FNF:5 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +DA:6,1 +DA:7,0 +DA:8,0 +DA:9,0 +DA:12,0 +DA:13,0 +DA:14,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:31,0 +DA:32,0 +DA:33,0 +DA:37,0 +DA:38,0 +DA:45,0 +DA:57,0 +LF:27 +LH:1 +BRDA:13,0,0,0 +BRDA:13,0,1,0 +BRDA:13,0,2,0 +BRDA:13,0,3,0 +BRDA:13,0,4,0 +BRDA:13,0,5,0 +BRDA:13,0,6,0 +BRDA:25,1,0,0 +BRDA:25,1,1,0 +BRDA:25,1,2,0 +BRDA:25,1,3,0 +BRDA:25,1,4,0 +BRDA:25,1,5,0 +BRDA:25,1,6,0 +BRDA:25,1,7,0 +BRDA:37,2,0,0 +BRDA:37,2,1,0 +BRDA:55,3,0,0 +BRDA:55,3,1,0 +BRDA:78,4,0,0 +BRDA:78,4,1,0 +BRDA:85,5,0,0 +BRDA:85,5,1,0 +BRF:23 +BRH:0 +end_of_record +TN: +SF:src/pages/CandidateDetails.js +FN:7,(anonymous_0) +FN:10,(anonymous_1) +FN:111,(anonymous_2) +FNF:3 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +DA:7,1 +DA:8,0 +DA:10,0 +DA:11,0 +DA:12,0 +DA:15,0 +DA:16,0 +DA:23,0 +DA:24,0 +DA:34,0 +DA:112,0 +LF:11 +LH:1 +BRDA:15,0,0,0 +BRDA:15,0,1,0 +BRDA:23,1,0,0 +BRDA:23,1,1,0 +BRDA:51,2,0,0 +BRDA:51,2,1,0 +BRDA:58,3,0,0 +BRDA:58,3,1,0 +BRDA:65,4,0,0 +BRDA:65,4,1,0 +BRDA:74,5,0,0 +BRDA:74,5,1,0 +BRDA:83,6,0,0 +BRDA:83,6,1,0 +BRDA:97,7,0,0 +BRDA:97,7,1,0 +BRDA:106,8,0,0 +BRDA:106,8,1,0 +BRDA:106,8,2,0 +BRDA:129,9,0,0 +BRDA:129,9,1,0 +BRDA:134,10,0,0 +BRDA:134,10,1,0 +BRF:23 +BRH:0 +end_of_record +TN: +SF:src/pages/Candidates.js +FN:7,(anonymous_0) +FN:15,(anonymous_1) +FN:17,(anonymous_2) +FN:25,(anonymous_3) +FN:32,(anonymous_4) +FN:37,(anonymous_5) +FN:161,(anonymous_6) +FN:204,(anonymous_7) +FNF:8 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +DA:7,1 +DA:8,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:18,0 +DA:21,0 +DA:22,0 +DA:25,0 +DA:26,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:48,0 +DA:49,0 +DA:56,0 +DA:162,0 +DA:205,0 +LF:26 +LH:1 +BRDA:18,0,0,0 +BRDA:18,0,1,0 +BRDA:38,1,0,0 +BRDA:38,1,1,0 +BRDA:38,1,2,0 +BRDA:38,1,3,0 +BRDA:38,1,4,0 +BRDA:38,1,5,0 +BRDA:48,2,0,0 +BRDA:48,2,1,0 +BRDA:160,3,0,0 +BRDA:160,3,1,0 +BRDA:178,4,0,0 +BRDA:178,4,1,0 +BRDA:185,5,0,0 +BRDA:185,5,1,0 +BRDA:193,6,0,0 +BRDA:193,6,1,0 +BRDA:201,7,0,0 +BRDA:201,7,1,0 +BRDA:201,7,2,0 +BRDA:212,8,0,0 +BRDA:212,8,1,0 +BRDA:221,9,0,0 +BRDA:221,9,1,0 +BRDA:244,10,0,0 +BRDA:244,10,1,0 +BRDA:244,10,2,0 +BRF:28 +BRH:0 +end_of_record +TN: +SF:src/pages/CreateJob.js +FN:8,(anonymous_0) +FN:27,(anonymous_1) +FN:31,(anonymous_2) +FN:35,(anonymous_3) +FN:40,(anonymous_4) +FN:42,(anonymous_5) +FN:48,(anonymous_6) +FN:49,(anonymous_7) +FN:51,(anonymous_8) +FN:55,(anonymous_9) +FN:56,(anonymous_10) +FN:62,(anonymous_11) +FN:63,(anonymous_12) +FN:65,(anonymous_13) +FN:69,(anonymous_14) +FN:75,(anonymous_15) +FN:76,(anonymous_16) +FN:77,(anonymous_17) +FN:78,(anonymous_18) +FN:91,(anonymous_19) +FN:190,(anonymous_20) +FN:196,(anonymous_21) +FN:202,(anonymous_22) +FN:212,(anonymous_23) +FN:224,(anonymous_24) +FN:230,(anonymous_25) +FN:236,(anonymous_26) +FN:246,(anonymous_27) +FN:372,(anonymous_28) +FN:378,(anonymous_29) +FN:384,(anonymous_30) +FN:394,(anonymous_31) +FN:406,(anonymous_32) +FN:412,(anonymous_33) +FN:418,(anonymous_34) +FN:428,(anonymous_35) +FN:443,(anonymous_36) +FNF:37 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +FNDA:0,(anonymous_11) +FNDA:0,(anonymous_12) +FNDA:0,(anonymous_13) +FNDA:0,(anonymous_14) +FNDA:0,(anonymous_15) +FNDA:0,(anonymous_16) +FNDA:0,(anonymous_17) +FNDA:0,(anonymous_18) +FNDA:0,(anonymous_19) +FNDA:0,(anonymous_20) +FNDA:0,(anonymous_21) +FNDA:0,(anonymous_22) +FNDA:0,(anonymous_23) +FNDA:0,(anonymous_24) +FNDA:0,(anonymous_25) +FNDA:0,(anonymous_26) +FNDA:0,(anonymous_27) +FNDA:0,(anonymous_28) +FNDA:0,(anonymous_29) +FNDA:0,(anonymous_30) +FNDA:0,(anonymous_31) +FNDA:0,(anonymous_32) +FNDA:0,(anonymous_33) +FNDA:0,(anonymous_34) +FNDA:0,(anonymous_35) +FNDA:0,(anonymous_36) +DA:8,1 +DA:9,0 +DA:10,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:32,0 +DA:33,0 +DA:36,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:48,0 +DA:49,0 +DA:51,0 +DA:55,0 +DA:56,0 +DA:62,0 +DA:63,0 +DA:65,0 +DA:69,0 +DA:70,0 +DA:73,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:84,0 +DA:87,0 +DA:91,0 +DA:191,0 +DA:196,0 +DA:202,0 +DA:212,0 +DA:225,0 +DA:230,0 +DA:236,0 +DA:246,0 +DA:373,0 +DA:378,0 +DA:384,0 +DA:394,0 +DA:407,0 +DA:412,0 +DA:418,0 +DA:428,0 +DA:443,0 +LF:47 +LH:1 +BRDA:36,0,0,0 +BRDA:36,0,1,0 +BRDA:44,1,0,0 +BRDA:44,1,1,0 +BRDA:51,2,0,0 +BRDA:51,2,1,0 +BRDA:79,3,0,0 +BRDA:79,3,1,0 +BRDA:80,4,0,0 +BRDA:80,4,1,0 +BRDA:81,5,0,0 +BRDA:81,5,1,0 +BRDA:199,6,0,0 +BRDA:199,6,1,0 +BRDA:233,7,0,0 +BRDA:233,7,1,0 +BRDA:381,8,0,0 +BRDA:381,8,1,0 +BRDA:415,9,0,0 +BRDA:415,9,1,0 +BRDA:454,10,0,0 +BRDA:454,10,1,0 +BRF:22 +BRH:0 +end_of_record +TN: +SF:src/pages/Dashboard.js +FN:15,(anonymous_0) +FN:18,(anonymous_1) +FN:34,(anonymous_2) +FN:39,(anonymous_3) +FN:74,(anonymous_4) +FN:106,(anonymous_5) +FN:141,(anonymous_6) +FN:174,(anonymous_7) +FNF:8 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +DA:15,1 +DA:16,0 +DA:18,0 +DA:19,0 +DA:26,0 +DA:34,0 +DA:35,0 +DA:36,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:44,0 +DA:47,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:81,0 +DA:82,0 +DA:93,0 +DA:107,0 +DA:142,0 +DA:175,0 +LF:25 +LH:1 +BRDA:22,0,0,0 +BRDA:22,0,1,0 +BRDA:23,1,0,0 +BRDA:23,1,1,0 +BRDA:23,2,0,0 +BRDA:23,2,1,0 +BRDA:23,2,2,0 +BRDA:27,3,0,0 +BRDA:27,3,1,0 +BRDA:28,4,0,0 +BRDA:28,4,1,0 +BRDA:29,5,0,0 +BRDA:29,5,1,0 +BRDA:30,6,0,0 +BRDA:30,6,1,0 +BRDA:40,7,0,0 +BRDA:40,7,1,0 +BRDA:50,8,0,0 +BRDA:50,8,1,0 +BRDA:56,9,0,0 +BRDA:56,9,1,0 +BRDA:62,10,0,0 +BRDA:62,10,1,0 +BRDA:68,11,0,0 +BRDA:68,11,1,0 +BRDA:76,12,0,0 +BRDA:76,12,1,0 +BRDA:77,13,0,0 +BRDA:77,13,1,0 +BRDA:81,14,0,0 +BRDA:81,14,1,0 +BRDA:139,15,0,0 +BRDA:139,15,1,0 +BRDA:172,16,0,0 +BRDA:172,16,1,0 +BRDA:186,17,0,0 +BRDA:186,17,1,0 +BRDA:187,18,0,0 +BRDA:187,18,1,0 +BRDA:188,19,0,0 +BRDA:188,19,1,0 +BRDA:189,20,0,0 +BRDA:189,20,1,0 +BRDA:213,21,0,0 +BRDA:213,21,1,0 +BRDA:235,22,0,0 +BRDA:235,22,1,0 +BRF:47 +BRH:0 +end_of_record +TN: +SF:src/pages/EmployerDetails.js +FN:7,(anonymous_0) +FN:10,(anonymous_1) +FNF:2 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +DA:7,1 +DA:8,0 +DA:10,0 +DA:11,0 +DA:12,0 +DA:15,0 +DA:16,0 +DA:23,0 +DA:24,0 +DA:34,0 +LF:10 +LH:1 +BRDA:15,0,0,0 +BRDA:15,0,1,0 +BRDA:23,1,0,0 +BRDA:23,1,1,0 +BRDA:52,2,0,0 +BRDA:52,2,1,0 +BRDA:59,3,0,0 +BRDA:59,3,1,0 +BRDA:66,4,0,0 +BRDA:66,4,1,0 +BRDA:75,5,0,0 +BRDA:75,5,1,0 +BRDA:87,6,0,0 +BRDA:87,6,1,0 +BRDA:96,7,0,0 +BRDA:96,7,1,0 +BRF:16 +BRH:0 +end_of_record +TN: +SF:src/pages/Employers.js +FN:7,(anonymous_0) +FN:8,(anonymous_1) +FN:32,(anonymous_2) +FNF:3 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +DA:7,1 +DA:8,0 +DA:9,0 +DA:10,0 +DA:13,0 +DA:14,0 +DA:21,0 +DA:33,0 +LF:8 +LH:1 +BRDA:13,0,0,0 +BRDA:13,0,1,0 +BRDA:31,1,0,0 +BRDA:31,1,1,0 +BRDA:49,2,0,0 +BRDA:49,2,1,0 +BRDA:56,3,0,0 +BRDA:56,3,1,0 +BRDA:63,4,0,0 +BRDA:63,4,1,0 +BRDA:72,5,0,0 +BRDA:72,5,1,0 +BRF:12 +BRH:0 +end_of_record +TN: +SF:src/pages/JobDetails.js +FN:9,(anonymous_0) +FN:15,(anonymous_1) +FN:20,(anonymous_2) +FN:65,(anonymous_3) +FN:116,(anonymous_4) +FN:130,(anonymous_5) +FN:144,(anonymous_6) +FN:163,(anonymous_7) +FN:273,(anonymous_8) +FNF:9 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +DA:9,1 +DA:10,0 +DA:11,0 +DA:12,0 +DA:13,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:23,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:32,0 +DA:33,0 +DA:35,0 +DA:37,0 +DA:41,0 +DA:42,0 +DA:49,0 +DA:50,0 +DA:65,0 +DA:66,0 +DA:67,0 +DA:68,0 +DA:69,0 +DA:72,0 +DA:117,0 +DA:131,0 +DA:145,0 +DA:164,0 +DA:273,0 +LF:34 +LH:1 +BRDA:21,0,0,0 +BRDA:21,0,1,0 +BRDA:35,1,0,0 +BRDA:35,1,1,0 +BRDA:41,2,0,0 +BRDA:41,2,1,0 +BRDA:49,3,0,0 +BRDA:49,3,1,0 +BRDA:65,4,0,0 +BRDA:66,5,0,0 +BRDA:66,5,1,0 +BRDA:66,6,0,0 +BRDA:66,6,1,0 +BRDA:67,7,0,0 +BRDA:67,7,1,0 +BRDA:68,8,0,0 +BRDA:68,8,1,0 +BRDA:85,9,0,0 +BRDA:85,9,1,0 +BRDA:85,9,2,0 +BRDA:92,10,0,0 +BRDA:92,10,1,0 +BRDA:111,11,0,0 +BRDA:111,11,1,0 +BRDA:111,11,2,0 +BRDA:125,12,0,0 +BRDA:125,12,1,0 +BRDA:125,12,2,0 +BRDA:139,13,0,0 +BRDA:139,13,1,0 +BRDA:139,13,2,0 +BRDA:158,14,0,0 +BRDA:158,14,1,0 +BRDA:158,14,2,0 +BRDA:183,15,0,0 +BRDA:183,15,1,0 +BRDA:207,16,0,0 +BRDA:207,16,1,0 +BRDA:227,17,0,0 +BRDA:227,17,1,0 +BRDA:247,18,0,0 +BRDA:247,18,1,0 +BRDA:250,19,0,0 +BRDA:250,19,1,0 +BRDA:258,20,0,0 +BRDA:258,20,1,0 +BRDA:258,20,2,0 +BRDA:278,21,0,0 +BRDA:278,21,1,0 +BRDA:281,22,0,0 +BRDA:281,22,1,0 +BRF:51 +BRH:0 +end_of_record +TN: +SF:src/pages/Jobs.js +FN:8,(anonymous_0) +FN:23,(anonymous_1) +FN:25,(anonymous_2) +FN:33,(anonymous_3) +FN:40,(anonymous_4) +FN:45,(anonymous_5) +FN:52,(anonymous_6) +FN:80,(anonymous_7) +FN:209,(anonymous_8) +FN:251,(anonymous_9) +FNF:10 +FNH:5 +FNDA:3,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:1,(anonymous_5) +FNDA:1,(anonymous_6) +FNDA:1,(anonymous_7) +FNDA:1,(anonymous_8) +FNDA:0,(anonymous_9) +DA:8,2 +DA:9,3 +DA:10,3 +DA:23,3 +DA:24,0 +DA:25,0 +DA:26,0 +DA:29,0 +DA:30,0 +DA:33,3 +DA:34,0 +DA:40,3 +DA:41,0 +DA:42,0 +DA:45,3 +DA:46,1 +DA:47,1 +DA:48,1 +DA:49,1 +DA:52,3 +DA:53,1 +DA:54,1 +DA:55,0 +DA:56,0 +DA:57,0 +DA:61,3 +DA:62,1 +DA:73,2 +DA:74,1 +DA:80,1 +DA:89,1 +DA:210,1 +DA:252,0 +LF:33 +LH:21 +BRDA:26,0,0,0 +BRDA:26,0,1,0 +BRDA:45,1,0,1 +BRDA:46,2,0,0 +BRDA:46,2,1,1 +BRDA:46,3,0,1 +BRDA:46,3,1,0 +BRDA:47,4,0,0 +BRDA:47,4,1,1 +BRDA:48,5,0,0 +BRDA:48,5,1,1 +BRDA:53,6,0,1 +BRDA:53,6,1,0 +BRDA:53,6,2,0 +BRDA:53,6,3,0 +BRDA:61,7,0,1 +BRDA:61,7,1,2 +BRDA:73,8,0,1 +BRDA:73,8,1,1 +BRDA:77,9,0,1 +BRDA:77,9,1,0 +BRDA:98,10,0,1 +BRDA:98,10,1,0 +BRDA:98,10,2,1 +BRDA:208,11,0,1 +BRDA:208,11,1,0 +BRDA:232,12,0,1 +BRDA:232,12,1,0 +BRDA:248,13,0,1 +BRDA:248,13,1,0 +BRDA:248,13,2,0 +BRDA:259,14,0,0 +BRDA:259,14,1,0 +BRDA:284,15,0,1 +BRDA:284,15,1,0 +BRDA:284,15,2,0 +BRF:36 +BRH:17 +end_of_record +TN: +SF:src/pages/Login.js +FN:6,(anonymous_0) +FN:15,(anonymous_1) +FN:22,(anonymous_2) +FN:96,(anonymous_3) +FNF:4 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +DA:6,1 +DA:7,0 +DA:8,0 +DA:12,0 +DA:13,0 +DA:15,0 +DA:16,0 +DA:22,0 +DA:23,0 +DA:24,0 +DA:26,0 +DA:28,0 +DA:29,0 +DA:33,0 +DA:96,0 +LF:15 +LH:1 +BRDA:28,0,0,0 +BRDA:28,0,1,0 +BRDA:84,1,0,0 +BRDA:84,1,1,0 +BRDA:98,2,0,0 +BRDA:98,2,1,0 +BRDA:115,3,0,0 +BRDA:115,3,1,0 +BRF:8 +BRH:0 +end_of_record +TN: +SF:src/pages/Profile.js +FN:6,(anonymous_0) +FN:15,(anonymous_1) +FN:22,(anonymous_2) +FNF:3 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +DA:6,1 +DA:7,0 +DA:8,0 +DA:13,0 +DA:15,0 +DA:16,0 +DA:22,0 +DA:23,0 +DA:24,0 +DA:26,0 +DA:27,0 +DA:36,0 +DA:37,0 +DA:39,0 +DA:41,0 +DA:45,0 +LF:16 +LH:1 +BRDA:9,0,0,0 +BRDA:9,0,1,0 +BRDA:10,1,0,0 +BRDA:10,1,1,0 +BRDA:11,2,0,0 +BRDA:11,2,1,0 +BRDA:132,3,0,0 +BRDA:132,3,1,0 +BRF:8 +BRH:0 +end_of_record +TN: +SF:src/pages/Register.js +FN:6,(anonymous_0) +FN:20,(anonymous_1) +FN:27,(anonymous_2) +FN:179,(anonymous_3) +FN:214,(anonymous_4) +FNF:5 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +DA:6,1 +DA:7,0 +DA:8,0 +DA:16,0 +DA:17,0 +DA:18,0 +DA:20,0 +DA:21,0 +DA:27,0 +DA:28,0 +DA:30,0 +DA:31,0 +DA:32,0 +DA:35,0 +DA:36,0 +DA:37,0 +DA:40,0 +DA:42,0 +DA:50,0 +DA:51,0 +DA:55,0 +DA:179,0 +DA:214,0 +LF:23 +LH:1 +BRDA:30,0,0,0 +BRDA:30,0,1,0 +BRDA:35,1,0,0 +BRDA:35,1,1,0 +BRDA:50,2,0,0 +BRDA:50,2,1,0 +BRDA:167,3,0,0 +BRDA:167,3,1,0 +BRDA:181,4,0,0 +BRDA:181,4,1,0 +BRDA:202,5,0,0 +BRDA:202,5,1,0 +BRDA:216,6,0,0 +BRDA:216,6,1,0 +BRDA:233,7,0,0 +BRDA:233,7,1,0 +BRF:16 +BRH:0 +end_of_record +TN: +SF:src/pages/Resumes.js +FN:7,(anonymous_0) +FN:11,(anonymous_1) +FN:17,(anonymous_2) +FN:41,(anonymous_3) +FN:69,(anonymous_4) +FN:88,(anonymous_5) +FN:98,(anonymous_6) +FN:176,(anonymous_7) +FN:204,(anonymous_8) +FN:212,(anonymous_9) +FN:220,(anonymous_10) +FNF:11 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:0,(anonymous_6) +FNDA:0,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:0,(anonymous_10) +DA:7,1 +DA:8,0 +DA:9,0 +DA:11,0 +DA:14,0 +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:25,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:37,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:52,0 +DA:53,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:63,0 +DA:65,0 +DA:69,0 +DA:70,0 +DA:71,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:79,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:84,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:91,0 +DA:92,0 +DA:94,0 +DA:98,0 +DA:99,0 +DA:100,0 +DA:103,0 +DA:104,0 +DA:105,0 +DA:106,0 +DA:108,0 +DA:112,0 +DA:113,0 +DA:120,0 +DA:177,0 +DA:204,0 +DA:212,0 +DA:220,0 +LF:64 +LH:1 +BRDA:19,0,0,0 +BRDA:19,0,1,0 +BRDA:20,1,0,0 +BRDA:20,1,1,0 +BRDA:32,2,0,0 +BRDA:32,2,1,0 +BRDA:42,3,0,0 +BRDA:42,3,1,0 +BRDA:50,4,0,0 +BRDA:50,4,1,0 +BRDA:63,5,0,0 +BRDA:63,5,1,0 +BRDA:78,6,0,0 +BRDA:78,6,1,0 +BRDA:99,7,0,0 +BRDA:99,7,1,0 +BRDA:112,8,0,0 +BRDA:112,8,1,0 +BRDA:150,9,0,0 +BRDA:150,9,1,0 +BRDA:165,10,0,0 +BRDA:165,10,1,0 +BRDA:175,11,0,0 +BRDA:175,11,1,0 +BRDA:190,12,0,0 +BRDA:190,12,1,0 +BRDA:210,13,0,0 +BRDA:210,13,1,0 +BRF:28 +BRH:0 +end_of_record diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8467df0..7985749 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -28,6 +28,7 @@ "devDependencies": { "@types/react": "^18.2.42", "@types/react-dom": "^18.2.17", + "eslint": "^8.57.0", "typescript": "4.9.5" } }, diff --git a/frontend/package.json b/frontend/package.json index 0666e77..7965702 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,7 +25,8 @@ "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "lint": "eslint --ext js,jsx src" }, "eslintConfig": { "extends": [ @@ -48,7 +49,8 @@ "devDependencies": { "@types/react": "^18.2.42", "@types/react-dom": "^18.2.17", - "typescript": "4.9.5" + "typescript": "4.9.5", + "eslint": "^8.57.0" }, "proxy": "http://merchantsofhope-supplyanddemandportal-backend:3001" } diff --git a/frontend/src/App.test.js b/frontend/src/App.test.js index 112488d..9ae3663 100644 --- a/frontend/src/App.test.js +++ b/frontend/src/App.test.js @@ -1,5 +1,5 @@ import React from 'react'; -import ReactDOMServer from 'react-dom/server'; +import { render, screen } from '@testing-library/react'; import App from './App'; jest.mock('axios', () => ({ @@ -49,7 +49,7 @@ jest.mock('react-hot-toast', () => ({ describe('App', () => { it('renders without crashing', () => { - const markup = ReactDOMServer.renderToStaticMarkup(); - expect(markup).toContain('data-testid="toaster"'); + render(); + expect(screen.getByTestId('toaster')).toBeInTheDocument(); }); }); diff --git a/frontend/src/components/Layout.test.js b/frontend/src/components/Layout.test.js index 5a2b0d4..71bed9b 100644 --- a/frontend/src/components/Layout.test.js +++ b/frontend/src/components/Layout.test.js @@ -1,5 +1,6 @@ import React from 'react'; -import ReactDOMServer from 'react-dom/server'; +import { render, screen } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; import Layout from './Layout'; // Mock the AuthContext @@ -28,40 +29,30 @@ jest.mock('react-router-dom', () => ({ describe('Layout', () => { const renderLayout = () => { - const markup = ReactDOMServer.renderToStaticMarkup(); - const container = document.createElement('div'); - container.innerHTML = markup; - return container; + return render( + + + + ); }; - beforeEach(() => { - mockUseAuth.logout.mockClear(); - }); - it('renders the layout with user information', () => { - const container = renderLayout(); - const branding = Array.from(container.querySelectorAll('h1')) - .map((node) => node.textContent); - expect(branding).toContain('MerchantsOfHope-SupplyANdDemandPortal'); - - const textContent = container.textContent || ''; - expect(textContent).toContain('John Doe'); - expect(textContent).toContain('candidate'); + renderLayout(); + expect(screen.getAllByText('MerchantsOfHope-SupplyANdDemandPortal').length).toBeGreaterThan(0); + expect(screen.getAllByText('John Doe').length).toBeGreaterThan(0); + expect(screen.getAllByText('candidate').length).toBeGreaterThan(0); }); it('renders navigation items for candidate role', () => { - const container = renderLayout(); - const navTexts = Array.from(container.querySelectorAll('a')) - .map((link) => link.textContent.replace(/\s+/g, ' ').trim()) - .filter(Boolean); - - expect(navTexts).toEqual(expect.arrayContaining(['Dashboard', 'Jobs', 'Applications', 'Resumes'])); + renderLayout(); + expect(screen.getAllByText('Dashboard').length).toBeGreaterThan(0); + expect(screen.getAllByText('Jobs').length).toBeGreaterThan(0); + expect(screen.getAllByText('Applications').length).toBeGreaterThan(0); + expect(screen.getAllByText('Resumes').length).toBeGreaterThan(0); }); it('renders logout button', () => { - const container = renderLayout(); - const buttons = Array.from(container.querySelectorAll('button')) - .map((button) => button.textContent?.trim()); - expect(buttons).toContain('Logout'); + renderLayout(); + expect(screen.getByRole('button', { name: /logout/i })).toBeInTheDocument(); }); }); diff --git a/frontend/src/contexts/AuthContext.test.js b/frontend/src/contexts/AuthContext.test.js new file mode 100644 index 0000000..fd4d7c2 --- /dev/null +++ b/frontend/src/contexts/AuthContext.test.js @@ -0,0 +1,87 @@ +import React from 'react'; +import { renderHook, act } from '@testing-library/react'; +import { AuthProvider, useAuth } from './AuthContext'; + +jest.mock('axios', () => { + const instance = { + post: jest.fn(), + get: jest.fn(), + defaults: { + headers: { + common: {} + } + } + }; + return instance; +}); + +const axios = require('axios'); + +jest.mock('react-hot-toast', () => ({ + success: jest.fn(), + error: jest.fn() +})); + +const wrapper = ({ children }) => {children}; + +describe('AuthContext', () => { + beforeEach(() => { + jest.clearAllMocks(); + localStorage.clear(); + axios.defaults.headers.common = {}; + }); + + it('logs in and stores token', async () => { + axios.post.mockResolvedValueOnce({ + data: { + token: 'token123', + user: { email: 'test@example.com', role: 'candidate' } + } + }); + + const { result } = renderHook(() => useAuth(), { wrapper }); + + await act(async () => { + const response = await result.current.login('test@example.com', 'password'); + expect(response.success).toBe(true); + }); + + expect(localStorage.getItem('token')).toBe('token123'); + expect(axios.defaults.headers.common.Authorization).toBe('Bearer token123'); + expect(result.current.user.email).toBe('test@example.com'); + }); + + it('handles login failure gracefully', async () => { + axios.post.mockRejectedValueOnce({ response: { data: { error: 'Invalid credentials' } } }); + + const { result } = renderHook(() => useAuth(), { wrapper }); + + await act(async () => { + const response = await result.current.login('test@example.com', 'bad'); + expect(response.success).toBe(false); + expect(response.error).toBe('Invalid credentials'); + }); + + expect(localStorage.getItem('token')).toBeNull(); + }); + + it('logs out and clears token', async () => { + axios.post.mockResolvedValueOnce({ data: { token: 'token123', user: { email: 'test@example.com' } } }); + + const { result } = renderHook(() => useAuth(), { wrapper }); + + await act(async () => { + await result.current.login('test@example.com', 'password'); + }); + + axios.post.mockResolvedValueOnce({ data: { message: 'ok' } }); + + await act(async () => { + await result.current.logout(); + }); + + expect(localStorage.getItem('token')).toBeNull(); + expect(axios.defaults.headers.common.Authorization).toBeUndefined(); + expect(result.current.user).toBeNull(); + }); +}); diff --git a/frontend/src/pages/Applications.js b/frontend/src/pages/Applications.js index 8786ec1..dbf67d6 100644 --- a/frontend/src/pages/Applications.js +++ b/frontend/src/pages/Applications.js @@ -1,7 +1,7 @@ import React from 'react'; import { useQuery } from 'react-query'; import axios from 'axios'; -import { FileText, Briefcase, Building, Clock, CheckCircle, XCircle, AlertCircle } from 'lucide-react'; +import { FileText, Building, Clock, CheckCircle, XCircle, AlertCircle } from 'lucide-react'; const Applications = () => { const { data, isLoading } = useQuery('applications', async () => { diff --git a/frontend/src/pages/CandidateDetails.js b/frontend/src/pages/CandidateDetails.js index 92c68ce..6019f58 100644 --- a/frontend/src/pages/CandidateDetails.js +++ b/frontend/src/pages/CandidateDetails.js @@ -2,7 +2,7 @@ import React from 'react'; import { useParams } from 'react-router-dom'; import { useQuery } from 'react-query'; import axios from 'axios'; -import { MapPin, Mail, Phone, Linkedin, Github, Globe, User } from 'lucide-react'; +import { MapPin, Phone, Linkedin, Github, Globe, User } from 'lucide-react'; const CandidateDetails = () => { const { id } = useParams(); diff --git a/frontend/src/pages/CreateJob.js b/frontend/src/pages/CreateJob.js index 0cb76a3..3b3fc49 100644 --- a/frontend/src/pages/CreateJob.js +++ b/frontend/src/pages/CreateJob.js @@ -1,13 +1,11 @@ import React, { useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { useAuth } from '../contexts/AuthContext'; import { useMutation } from 'react-query'; import axios from 'axios'; import toast from 'react-hot-toast'; import { ArrowLeft, Save, Plus, X } from 'lucide-react'; const CreateJob = () => { - const { user } = useAuth(); const navigate = useNavigate(); const [formData, setFormData] = useState({ title: '', diff --git a/frontend/src/pages/Dashboard.js b/frontend/src/pages/Dashboard.js index f690a8c..ada60d3 100644 --- a/frontend/src/pages/Dashboard.js +++ b/frontend/src/pages/Dashboard.js @@ -9,9 +9,7 @@ import { FileText, Building, TrendingUp, - Clock, - CheckCircle, - AlertCircle + Clock } from 'lucide-react'; const Dashboard = () => { @@ -83,7 +81,11 @@ const Dashboard = () => { if (isLoading) { return (
-
+
); } @@ -97,9 +99,6 @@ const Dashboard = () => {

Welcome to your MerchantsOfHope-SupplyANdDemandPortal dashboard

-
- Debug: User role = {user?.role || 'undefined'}, User ID = {user?.id || 'undefined'} -
{/* Stats Cards */} diff --git a/frontend/src/pages/EmployerDetails.js b/frontend/src/pages/EmployerDetails.js index 06e1e47..319d9e5 100644 --- a/frontend/src/pages/EmployerDetails.js +++ b/frontend/src/pages/EmployerDetails.js @@ -2,7 +2,7 @@ import React from 'react'; import { useParams } from 'react-router-dom'; import { useQuery } from 'react-query'; import axios from 'axios'; -import { Building, MapPin, Users, Globe, Mail, Phone } from 'lucide-react'; +import { Building, MapPin, Users, Globe, Phone } from 'lucide-react'; const EmployerDetails = () => { const { id } = useParams(); diff --git a/frontend/src/pages/Employers.js b/frontend/src/pages/Employers.js index 47926fb..19da954 100644 --- a/frontend/src/pages/Employers.js +++ b/frontend/src/pages/Employers.js @@ -2,7 +2,7 @@ import React from 'react'; import { Link } from 'react-router-dom'; import { useQuery } from 'react-query'; import axios from 'axios'; -import { Building, MapPin, Users, Globe } from 'lucide-react'; +import { Building, Users, Globe } from 'lucide-react'; const Employers = () => { const { data, isLoading } = useQuery('employers', async () => { diff --git a/frontend/src/pages/Jobs.js b/frontend/src/pages/Jobs.js index 182f202..3e10b1b 100644 --- a/frontend/src/pages/Jobs.js +++ b/frontend/src/pages/Jobs.js @@ -14,7 +14,13 @@ const Jobs = () => { experienceLevel: '' }); - const { data, isLoading, refetch } = useQuery(['jobs', filters], async () => { + const { + data, + isLoading, + isError, + error, + refetch + } = useQuery(['jobs', filters], async () => { const params = new URLSearchParams(); Object.entries(filters).forEach(([key, value]) => { if (value) params.append(key, value); @@ -55,7 +61,27 @@ const Jobs = () => { if (isLoading) { return (
-
+
+
+ ); + } + + if (isError) { + return ( +
+

Unable to load jobs

+

{error?.message || 'Please try again later.'}

+
); } @@ -69,10 +95,6 @@ const Jobs = () => { Find your next career opportunity

- {/* Debug info */} -
- Debug: User role = {user?.role || 'undefined'} -
{(user?.role === 'employer' || user?.role === 'recruiter') && ( ({ + get: jest.fn() +})); + +jest.mock('../contexts/AuthContext', () => ({ + useAuth: () => ({ user: { role: 'employer' } }) +})); + +jest.mock('../contexts/AuthContext', () => ({ + useAuth: () => ({ user: { role: 'employer' } }) +})); + +const mockUseQuery = jest.fn(); + +jest.mock('react-query', () => ({ + useQuery: (...args) => mockUseQuery(...args) +})); + +const renderJobs = () => { + render( + + + + ); +}; + +describe('Jobs page', () => { + afterEach(() => { + mockUseQuery.mockReset(); + }); + + it('shows loading spinner while fetching', () => { + mockUseQuery.mockReturnValue({ isLoading: true }); + renderJobs(); + expect(screen.getByRole('status')).toBeInTheDocument(); + }); + + it('renders error state and allows retry', () => { + const refetch = jest.fn(); + mockUseQuery.mockReturnValue({ isLoading: false, isError: true, error: new Error('Boom'), refetch }); + renderJobs(); + + expect(screen.getByText(/unable to load jobs/i)).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: /retry/i })); + expect(refetch).toHaveBeenCalled(); + }); + + it('lists jobs when data is available', () => { + mockUseQuery.mockReturnValue({ + isLoading: false, + isError: false, + data: { + jobs: [{ id: '1', title: 'Engineer', status: 'active', company_name: 'Acme', location: 'Remote', employment_type: 'full-time', salary_min: 50000, salary_max: 80000 }] + }, + refetch: jest.fn() + }); + + renderJobs(); + + expect(screen.getByText('Engineer')).toBeInTheDocument(); + expect(screen.queryByText(/Debug: User role/)).not.toBeInTheDocument(); + }); +}); diff --git a/scripts/run-ci-tests.sh b/scripts/run-ci-tests.sh index 08035b5..e55507b 100755 --- a/scripts/run-ci-tests.sh +++ b/scripts/run-ci-tests.sh @@ -1,23 +1,16 @@ #!/usr/bin/env bash set -euo pipefail -COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.test.yml}" +echo ">>> Running backend lint locally" +(cd backend && npm run lint) -cleanup() { - docker compose -f "${COMPOSE_FILE}" down -v >/dev/null 2>&1 || true -} +echo ">>> Running frontend lint locally" +(cd frontend && npm run lint) -cleanup -trap cleanup EXIT +echo ">>> Running backend test suite" +(cd backend && npm test -- --runInBand --coverage) -run_stage() { - local service="$1" - echo ">>> Running ${service} tests inside container" - docker compose -f "${COMPOSE_FILE}" build "${service}" - docker compose -f "${COMPOSE_FILE}" run --rm "${service}" -} +echo ">>> Running frontend test suite" +(cd frontend && npm test -- --watchAll=false --coverage) -run_stage backend-tester -run_stage frontend-tester - -echo "All CI test stages completed successfully (containers only)." +echo "All CI test stages completed successfully."