diff --git a/.github/ISSUE_TEMPLATE/blank-issue.md b/.github/ISSUE_TEMPLATE/blank-issue.md index a75a374f..9049e260 100644 --- a/.github/ISSUE_TEMPLATE/blank-issue.md +++ b/.github/ISSUE_TEMPLATE/blank-issue.md @@ -2,7 +2,8 @@ name: Blank Issue about: Consistent formatting make Issues concise and easy to navigate title: '' -labels: '' +labels: 'feature: missing, level: missing, milestone: missing, priority: missing, + role: missing, size: missing' assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 00000000..9c51bf88 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,51 @@ +--- +name: Bug report +about: Lists out information that would be help developers quickly identify and fix + potentail problems. +title: 'Bug: ' +labels: 'bug, feature: missing, level: missing, milestone: missing, priority: MUST + HAVE, role: missing, size: missing' +assignees: '' + +--- + +#### Describe the bug + +REPLACE THIS TEXT - A clear and concise description of what the bug is, who it affects, how it affects users. + +(optional) REPLACE THIS TEXT - What is the user's goal? + + +#### Steps to reproduce the issue + +(if applicable, please specify platform (iOS, Android, Windows, Mac version) and brower) + +REPLACE THE FOLLOWNG TEXT: +- 1. Go to '...' +- 2. Click on '....' +- 3. Scroll down to '....' +- 4. See error + + +#### What's the expected result? + +REPLACE THIS TEXT - What is supposed to happen after following the above steps? + +(if you have a suggested solution, you can add it here) + +#### What's the actual result? + +REPLACE THIS TEXT - What is supposed actually happening after following the above steps? + +#### Additional details / screenshot + +REPLACE THIS TEXT - Screenshot (drag in) +REPLACE THIS TEXT - Video recording (drag in) + +#### Device configuration + +REPLACE THE FOLLOWNG TEXT: +- Device: [e.g. type of smartphone, tablet, desktop computer] +- OS version: [e.g. iOS, Android version #] +- Browser [e.g. Chrome, Firefox, Safari, etc.] +- Browser version [e.g. 22] diff --git a/.github/ISSUE_TEMPLATE/control-what-appears-when-you-paste-your-sites-link-in-social-media-sites.md b/.github/ISSUE_TEMPLATE/control-what-appears-when-you-paste-your-sites-link-in-social-media-sites.md deleted file mode 100644 index 874831b0..00000000 --- a/.github/ISSUE_TEMPLATE/control-what-appears-when-you-paste-your-sites-link-in-social-media-sites.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: Control what appears when you paste your sites link in social media sites -about: Add Open Graph Markup tags to header -title: Control what appears when you paste your sites link in social media sites -labels: Tutorial, enhancement, question -assignees: '' - ---- - -### Overview -When your website is shared on slack, facebook, twitter, etc. It should automatically display with an image and title instead of just the URL. - -### Action items -Identify what to put in the following fields: -og:url -g:type -og:title -og:description -og:image -og:type (optional) -og:local (option) -using the standards set forth in the instructions. - -Add content to header and test with the tool provided in the instructions. - -### Instructions -[A Guide to Sharing for Webmasters](https://developers.facebook.com/docs/sharing/webmasters#markup) diff --git a/.github/ISSUE_TEMPLATE/feature--request.md b/.github/ISSUE_TEMPLATE/feature--request.md index 59ecbf52..4f2ec342 100644 --- a/.github/ISSUE_TEMPLATE/feature--request.md +++ b/.github/ISSUE_TEMPLATE/feature--request.md @@ -2,7 +2,7 @@ name: Feature request about: Suggest an idea for this project title: 'Feature Suggestion: ' -labels: documentation, product +labels: documentation assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/lighthouse--accessibility---forms.md b/.github/ISSUE_TEMPLATE/lighthouse--accessibility---forms.md index aed87275..54a28b7a 100644 --- a/.github/ISSUE_TEMPLATE/lighthouse--accessibility---forms.md +++ b/.github/ISSUE_TEMPLATE/lighthouse--accessibility---forms.md @@ -3,7 +3,7 @@ name: 'Lighthouse: Accessibility - Forms' about: Instructions for creating or improving forms to make them accessible when visitors use screen readers AKA Form elements must have labels title: 'Lighthouse: Accessibility - Forms' -labels: Accessibility, Site Audits, Tutorial +labels: '' assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/lighthouse--accessibility---links.md b/.github/ISSUE_TEMPLATE/lighthouse--accessibility---links.md index c10ecb81..f664248c 100644 --- a/.github/ISSUE_TEMPLATE/lighthouse--accessibility---links.md +++ b/.github/ISSUE_TEMPLATE/lighthouse--accessibility---links.md @@ -2,7 +2,7 @@ name: 'Lighthouse: Accessibility - Links' about: AKA Links must have discernible text title: 'Lighthouse: Accessibility - Links' -labels: Accessibility, Site Audits, Tutorial +labels: '' assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/lighthouse--cross-origin-destinations-are-unsafe.md b/.github/ISSUE_TEMPLATE/lighthouse--cross-origin-destinations-are-unsafe.md index b2831e53..455b6311 100644 --- a/.github/ISSUE_TEMPLATE/lighthouse--cross-origin-destinations-are-unsafe.md +++ b/.github/ISSUE_TEMPLATE/lighthouse--cross-origin-destinations-are-unsafe.md @@ -2,7 +2,7 @@ name: 'Lighthouse: Cross-origin destinations are unsafe' about: Instructions for addressing the cross-origin linking vulnerabilities title: 'Lighthouse Issue: Cross-origin destinations are unsafe' -labels: Performance, Security, Site Audits +labels: '' assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/lighthouse--how-to.md b/.github/ISSUE_TEMPLATE/lighthouse--how-to.md index 246ea9be..c99a39ca 100644 --- a/.github/ISSUE_TEMPLATE/lighthouse--how-to.md +++ b/.github/ISSUE_TEMPLATE/lighthouse--how-to.md @@ -2,7 +2,7 @@ name: 'Lighthouse: How To' about: Provides overview of how to use Lighthouse and links to additional resources title: 'Lighthouse: How To' -labels: Performance, Security, Site Audits, Tutorial +labels: '' assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/lighthouse--image-optimization.md b/.github/ISSUE_TEMPLATE/lighthouse--image-optimization.md index a512c3cf..5fc0ab9e 100644 --- a/.github/ISSUE_TEMPLATE/lighthouse--image-optimization.md +++ b/.github/ISSUE_TEMPLATE/lighthouse--image-optimization.md @@ -2,7 +2,7 @@ name: 'Lighthouse: Image Optimization' about: Instructions for optimizing images title: 'Lighthouse: Image Optimization' -labels: Performance, Site Audits, Tutorial +labels: '' assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/post-an-open-role.md b/.github/ISSUE_TEMPLATE/post-an-open-role.md new file mode 100644 index 00000000..eef2c760 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/post-an-open-role.md @@ -0,0 +1,12 @@ +--- +name: Post an open role +about: Recruit volunteers for specific open roles template +title: 'TDM: Open Role for: [Replace with NAME OF ROLE]' +labels: 'feature: recruiting, level: easy, role: missing, size: 0.25pt' +assignees: '' + +--- + + + +[INSERT DRAFT FROM THE Recruit volunteers for team open roles issue] diff --git a/.github/ISSUE_TEMPLATE/wave-chrome-extension--accessibility-review.md b/.github/ISSUE_TEMPLATE/wave-chrome-extension--accessibility-review.md index da9c14d8..7a8184af 100644 --- a/.github/ISSUE_TEMPLATE/wave-chrome-extension--accessibility-review.md +++ b/.github/ISSUE_TEMPLATE/wave-chrome-extension--accessibility-review.md @@ -2,7 +2,7 @@ name: 'Wave Chrome Extension: Accessibility review' about: Describe this issue template's purpose here. title: 'Wave Chrome Extension: Accessibility review' -labels: Accessibility, Performance, Site Audits, Tutorial +labels: '' assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/which-accessibility-testing-tool-should-you-use-.md b/.github/ISSUE_TEMPLATE/which-accessibility-testing-tool-should-you-use-.md index bffab3bd..1658c1c0 100644 --- a/.github/ISSUE_TEMPLATE/which-accessibility-testing-tool-should-you-use-.md +++ b/.github/ISSUE_TEMPLATE/which-accessibility-testing-tool-should-you-use-.md @@ -3,7 +3,7 @@ name: Which accessibility testing tool should you use? about: There are a lot of tools, this issue has a list of our favorites and links to more title: Which accessibility testing tool should you use? -labels: Accessibility, Performance, Security, Site Audits, Tutorial +labels: '' assignees: '' --- diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 89307b33..8a5743c0 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -2,7 +2,7 @@ on: push: branches: - - experimental + - uat - develop - main release: diff --git a/.github/workflows/new-issue-create-card.yml b/.github/workflows/new-issue-create-card.yml deleted file mode 100644 index aa2bdc12..00000000 --- a/.github/workflows/new-issue-create-card.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: Create card for new issues -on: - issues: - types: [opened] -jobs: - createCard: - runs-on: ubuntu-latest - steps: - - name: Create or Update Project Card - uses: peter-evans/create-or-update-project-card@v2 - with: - project-name: Product Management - column-name: New Issue Approval diff --git a/client/.eslintrc.json b/client/.eslintrc.json index 9e243a5e..4f448619 100644 --- a/client/.eslintrc.json +++ b/client/.eslintrc.json @@ -19,7 +19,7 @@ "ecmaFeatures": { "jsx": true }, - "ecmaVersion": 2018, + "ecmaVersion": 2020, "sourceType": "module" }, "plugins": ["react", "jest", "prettier", "eslint-plugin-react"], diff --git a/client/package-lock.json b/client/package-lock.json index b3a1c521..92569145 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,31 +1,30 @@ { "name": "tdm-calculator-client", - "version": "0.2.48", + "version": "0.2.50", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "tdm-calculator-client", - "version": "0.2.48", + "version": "0.2.50", "dependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", - "@fortawesome/fontawesome-svg-core": "^6.1.1", - "@fortawesome/free-solid-svg-icons": "^6.1.1", - "@fortawesome/react-fontawesome": "^0.2.0", "@react-pdf/renderer": "^3.1.9", "axios": "^1.6.1", "clsx": "^2.0.0", "eslint": "^8.53.0", "formik": "^2.2.9", "interweave": "^13.1.0", - "moment": "^2.29.1", + "luxon": "^3.4.4", "react": "^18.1.0", "react-aria-modal": "^5.0.0", "react-beautiful-dnd": "^13.1.1", "react-csv": "^2.2.2", + "react-csv-downloader": "^3.1.0", "react-datepicker": "^4.24.0", "react-dom": "^18.1.0", "react-gtm-module": "^2.0.11", + "react-icons": "^5.2.1", "react-input-mask": "^2.0.4", "react-jss": "^10.8.2", "react-loader": "^2.4.7", @@ -2726,48 +2725,6 @@ "version": "0.1.6", "license": "MIT" }, - "node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.4.2", - "hasInstallScript": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "6.4.2", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "6.4.2", - "hasInstallScript": true, - "license": "(CC-BY-4.0 AND MIT)", - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/react-fontawesome": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", - "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", - "dependencies": { - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "@fortawesome/fontawesome-svg-core": "~1 || ~6", - "react": ">=16.3" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "license": "Apache-2.0", @@ -5382,12 +5339,13 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "1.20.1", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dev": true, - "license": "MIT", "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -5395,7 +5353,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -5406,24 +5364,27 @@ }, "node_modules/body-parser/node_modules/bytes": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/body-parser/node_modules/debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "license": "MIT", "dependencies": { "ms": "2.0.0" } }, "node_modules/body-parser/node_modules/iconv-lite": { "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, - "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -5433,8 +5394,9 @@ }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true }, "node_modules/bonjour-service": { "version": "1.1.1", @@ -5473,11 +5435,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -6052,8 +6015,9 @@ }, "node_modules/content-type": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -6064,9 +6028,10 @@ "license": "MIT" }, "node_modules/cookie": { - "version": "0.5.0", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -7130,9 +7095,10 @@ "license": "MIT" }, "node_modules/ejs": { - "version": "3.1.9", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "jake": "^10.8.5" }, @@ -8119,16 +8085,17 @@ } }, "node_modules/express": { - "version": "4.18.2", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dev": true, - "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -8268,6 +8235,11 @@ "webpack": "^4.0.0 || ^5.0.0" } }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, "node_modules/filelist": { "version": "1.0.4", "dev": true, @@ -8304,9 +8276,10 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -8415,9 +8388,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -9736,8 +9709,9 @@ }, "node_modules/is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -11821,6 +11795,14 @@ "yallist": "^3.0.2" } }, + "node_modules/luxon": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", + "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==", + "engines": { + "node": ">=12" + } + }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", @@ -11879,8 +11861,9 @@ }, "node_modules/media-typer": { "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -12063,13 +12046,6 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/moment": { - "version": "2.29.4", - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/ms": { "version": "2.1.2", "license": "MIT" @@ -14181,8 +14157,9 @@ }, "node_modules/qs": { "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.4" }, @@ -14281,9 +14258,10 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, - "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -14296,16 +14274,18 @@ }, "node_modules/raw-body/node_modules/bytes": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/raw-body/node_modules/iconv-lite": { "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, - "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -14377,6 +14357,20 @@ "version": "2.2.2", "license": "MIT" }, + "node_modules/react-csv-downloader": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-csv-downloader/-/react-csv-downloader-3.1.0.tgz", + "integrity": "sha512-ART4CNMU89Y5OZQp9gR3IUlVt541djWwRfokQY+CYsGQjHNZFJAk6FvgOwLUNG0o/CxeUdexYjnMMFinMv4Xjg==", + "dependencies": { + "file-saver": "^2.0.2" + }, + "engines": { + "npm": ">=7.0.0" + }, + "peerDependencies": { + "react": "^16.6.3 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-datepicker": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.24.0.tgz", @@ -14486,6 +14480,14 @@ "version": "2.0.11", "license": "MIT" }, + "node_modules/react-icons": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz", + "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-input-mask": { "version": "2.0.4", "license": "MIT", @@ -18022,8 +18024,9 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -18166,8 +18169,9 @@ }, "node_modules/type-is": { "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, - "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -18604,9 +18608,10 @@ } }, "node_modules/webpack-dev-middleware": { - "version": "5.3.3", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", "dev": true, - "license": "MIT", "dependencies": { "colorette": "^2.0.10", "memfs": "^3.4.3", @@ -20935,29 +20940,6 @@ "@floating-ui/utils": { "version": "0.1.6" }, - "@fortawesome/fontawesome-common-types": { - "version": "6.4.2" - }, - "@fortawesome/fontawesome-svg-core": { - "version": "6.4.2", - "requires": { - "@fortawesome/fontawesome-common-types": "6.4.2" - } - }, - "@fortawesome/free-solid-svg-icons": { - "version": "6.4.2", - "requires": { - "@fortawesome/fontawesome-common-types": "6.4.2" - } - }, - "@fortawesome/react-fontawesome": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", - "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", - "requires": { - "prop-types": "^15.8.1" - } - }, "@humanwhocodes/config-array": { "version": "0.11.13", "requires": { @@ -22712,11 +22694,13 @@ "dev": true }, "body-parser": { - "version": "1.20.1", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dev": true, "requires": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -22724,17 +22708,21 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, "dependencies": { "bytes": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true }, "debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -22742,6 +22730,8 @@ }, "iconv-lite": { "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -22749,6 +22739,8 @@ }, "ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } @@ -22784,10 +22776,12 @@ } }, "braces": { - "version": "3.0.2", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "brotli": { @@ -23148,6 +23142,8 @@ }, "content-type": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true }, "convert-source-map": { @@ -23155,7 +23151,9 @@ "dev": true }, "cookie": { - "version": "0.5.0", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "dev": true }, "cookie-signature": { @@ -23828,7 +23826,9 @@ "dev": true }, "ejs": { - "version": "3.1.9", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "requires": { "jake": "^10.8.5" @@ -24467,15 +24467,17 @@ "dev": true }, "express": { - "version": "4.18.2", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dev": true, "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -24581,6 +24583,11 @@ "schema-utils": "^3.0.0" } }, + "file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, "filelist": { "version": "1.0.4", "dev": true, @@ -24609,7 +24616,9 @@ "dev": true }, "fill-range": { - "version": "7.0.1", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -24685,9 +24694,9 @@ } }, "follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==" + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" }, "fontkit": { "version": "2.0.2", @@ -25475,6 +25484,8 @@ }, "is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, "is-number-object": { @@ -26967,6 +26978,11 @@ "yallist": "^3.0.2" } }, + "luxon": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", + "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==" + }, "lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", @@ -27009,6 +27025,8 @@ }, "media-typer": { "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true }, "memfs": { @@ -27113,9 +27131,6 @@ "minimist": "^1.2.6" } }, - "moment": { - "version": "2.29.4" - }, "ms": { "version": "2.1.2" }, @@ -28258,6 +28273,8 @@ }, "qs": { "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, "requires": { "side-channel": "^1.0.4" @@ -28324,7 +28341,9 @@ "dev": true }, "raw-body": { - "version": "2.5.1", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "requires": { "bytes": "3.1.2", @@ -28335,10 +28354,14 @@ "dependencies": { "bytes": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true }, "iconv-lite": { "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -28392,6 +28415,14 @@ "react-csv": { "version": "2.2.2" }, + "react-csv-downloader": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-csv-downloader/-/react-csv-downloader-3.1.0.tgz", + "integrity": "sha512-ART4CNMU89Y5OZQp9gR3IUlVt541djWwRfokQY+CYsGQjHNZFJAk6FvgOwLUNG0o/CxeUdexYjnMMFinMv4Xjg==", + "requires": { + "file-saver": "^2.0.2" + } + }, "react-datepicker": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.24.0.tgz", @@ -28477,6 +28508,11 @@ "react-gtm-module": { "version": "2.0.11" }, + "react-icons": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz", + "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==" + }, "react-input-mask": { "version": "2.0.4", "requires": { @@ -30943,6 +30979,8 @@ }, "to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { "is-number": "^7.0.0" @@ -31043,6 +31081,8 @@ }, "type-is": { "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, "requires": { "media-typer": "0.3.0", @@ -31327,7 +31367,9 @@ } }, "webpack-dev-middleware": { - "version": "5.3.3", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", "dev": true, "requires": { "colorette": "^2.0.10", diff --git a/client/package.json b/client/package.json index 1f2358b1..4a12da31 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "tdm-calculator-client", - "version": "0.2.50", + "version": "0.2.51", "private": true, "proxy": "http://localhost:5001", "scripts": { @@ -30,23 +30,22 @@ }, "dependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", - "@fortawesome/fontawesome-svg-core": "^6.1.1", - "@fortawesome/free-solid-svg-icons": "^6.1.1", - "@fortawesome/react-fontawesome": "^0.2.0", "@react-pdf/renderer": "^3.1.9", "axios": "^1.6.1", "clsx": "^2.0.0", "eslint": "^8.53.0", "formik": "^2.2.9", "interweave": "^13.1.0", - "moment": "^2.29.1", + "luxon": "^3.4.4", "react": "^18.1.0", "react-aria-modal": "^5.0.0", "react-beautiful-dnd": "^13.1.1", "react-csv": "^2.2.2", + "react-csv-downloader": "^3.1.0", "react-datepicker": "^4.24.0", "react-dom": "^18.1.0", "react-gtm-module": "^2.0.11", + "react-icons": "^5.2.1", "react-input-mask": "^2.0.4", "react-jss": "^10.8.2", "react-loader": "^2.4.7", diff --git a/client/src/App.js b/client/src/App.js index 083af825..5fbc354e 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -34,7 +34,9 @@ import ResetPassword from "./components/Authorization/ResetPassword"; import ForgotPassword from "./components/Authorization/ForgotPassword"; import Feedback from "./components/Feedback/FeedbackPage"; import ErrorPage from "./components/ErrorPage"; +import Offline from "./components/Offline"; import Logout from "./components/Authorization/Logout"; +import { getConfigs } from "./helpers/Config"; const calculationPath = "/calculation/:page/:projectId?/*"; @@ -51,6 +53,10 @@ const App = () => { } + loader={async () => { + const configs = await getConfigs(); + return { configs }; + }} > {/* These routes either have no sidebar or use a custom sidebar */} { } /> + } /> {/* Layout Route adds plain Sidebar */} { } > } /> - {/* TODO: update FAQ to use checklist link, redirect for now. */} } /> } /> diff --git a/client/src/components/About.js b/client/src/components/About.js index e3d4e100..ac806130 100644 --- a/client/src/components/About.js +++ b/client/src/components/About.js @@ -2,8 +2,7 @@ import React from "react"; import { createUseStyles } from "react-jss"; import packageInfo from "../../package.json"; import ContentContainer from "./Layout/ContentContainer"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons"; +import { MdLaunch } from "react-icons/md"; const useStyles = createUseStyles({ aboutContent: { @@ -14,7 +13,7 @@ const useStyles = createUseStyles({ }, externalLinkIcon: { fontSize: "14px", - padding: " 0 0.5em", + padding: " 0 0.4em", color: "#00F" } }); @@ -63,11 +62,7 @@ const About = () => {

Learn more about{" "} - Hack for LA{" "} - + Hack for LA

Status of the TDM Calculator

@@ -87,10 +82,7 @@ const About = () => { target="external" > Glossary - +
  • @@ -99,10 +91,7 @@ const About = () => { target="external" > Los Angeles City Planning, Mobility - +
  • @@ -111,10 +100,7 @@ const About = () => { target="external" > Proposed Ordinances and Initiatives - +
  • @@ -123,10 +109,7 @@ const About = () => { target="external" > Informational Video (January 2021) - +
  • @@ -135,10 +118,7 @@ const About = () => { target="external" > Fact Sheet - English (January 2021) - +
  • @@ -147,10 +127,7 @@ const About = () => { target="external" > Hoja Informativa - EspaƱol (Enero 2021) - +
  • diff --git a/client/src/components/ArchiveDelete/RolesArchive.js b/client/src/components/ArchiveDelete/RolesArchive.js index 38a1cd27..09717040 100644 --- a/client/src/components/ArchiveDelete/RolesArchive.js +++ b/client/src/components/ArchiveDelete/RolesArchive.js @@ -2,8 +2,7 @@ import React, { useState, useEffect } from "react"; import { Link } from "react-router-dom"; import { createUseStyles } from "react-jss"; import Popup from "reactjs-popup"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faTrash, faUndo } from "@fortawesome/free-solid-svg-icons"; +import { MdDelete, MdUndo } from "react-icons/md"; import * as accountService from "../../services/account.service"; import { useToast } from "../../contexts/Toast"; import RolesUnarchiveContextMenu from "./RolesUnarchiveContextMenu"; @@ -178,10 +177,7 @@ const RolesArchive = () => { - + } position="bottom center" @@ -208,10 +204,7 @@ const RolesArchive = () => { className={`${classes.optionsButton}`} style={{ color: "red" }} > - + } position="bottom center" diff --git a/client/src/components/ArchiveDelete/RolesContextMenu.js b/client/src/components/ArchiveDelete/RolesContextMenu.js index 0adb6283..5c4ea8ff 100644 --- a/client/src/components/ArchiveDelete/RolesContextMenu.js +++ b/client/src/components/ArchiveDelete/RolesContextMenu.js @@ -1,7 +1,6 @@ import React from "react"; import PropTypes from "prop-types"; -import { faArchive } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { MdArchive } from "react-icons/md"; import { createUseStyles } from "react-jss"; const useStyles = createUseStyles({ @@ -26,8 +25,7 @@ const RolesContextMenu = ({ user, handleArchiveUser }) => { className={classes.listItem} style={{ color: "red" }} > - diff --git a/client/src/components/ArchiveDelete/RolesDeleteContextMenu.js b/client/src/components/ArchiveDelete/RolesDeleteContextMenu.js index 3da939dd..9e88d3c4 100644 --- a/client/src/components/ArchiveDelete/RolesDeleteContextMenu.js +++ b/client/src/components/ArchiveDelete/RolesDeleteContextMenu.js @@ -1,7 +1,6 @@ import React from "react"; import PropTypes from "prop-types"; -import { faTrash } from "@fortawesome/free-solid-svg-icons"; // faRemove -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { MdDelete } from "react-icons/md"; import { createUseStyles } from "react-jss"; const useStyles = createUseStyles({ @@ -27,8 +26,7 @@ const RolesDeleteContextMenu = ({ user, handleDeleteUser }) => { className={classes.listItem} style={{ color: "red" }} > - diff --git a/client/src/components/ArchiveDelete/RolesUnarchiveContextMenu.js b/client/src/components/ArchiveDelete/RolesUnarchiveContextMenu.js index 5f12fa6f..69679cb6 100644 --- a/client/src/components/ArchiveDelete/RolesUnarchiveContextMenu.js +++ b/client/src/components/ArchiveDelete/RolesUnarchiveContextMenu.js @@ -1,7 +1,6 @@ import React from "react"; import PropTypes from "prop-types"; -import { faUndo } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { MdUndo } from "react-icons/md"; import { createUseStyles } from "react-jss"; const useStyles = createUseStyles({ @@ -27,8 +26,7 @@ const RolesArchiveContextMenu = ({ user, handleUnarchiveUser }) => { className={classes.listItem} style={{ color: "black" }} > - diff --git a/client/src/components/Authorization/UpdateAccount.js b/client/src/components/Authorization/UpdateAccount.js index 4c0645e1..733c3778 100644 --- a/client/src/components/Authorization/UpdateAccount.js +++ b/client/src/components/Authorization/UpdateAccount.js @@ -24,7 +24,6 @@ const UpdateAccount = props => { const classes = useStyles(); const params = useParams(); const initialValues = { - id: account.id || "", firstName: account.firstName || "", lastName: account.lastName || "", email: params.email || "" @@ -42,12 +41,11 @@ const UpdateAccount = props => { }); const handleSubmit = async ( - { id, firstName, lastName, email }, + { firstName, lastName, email }, { setSubmitting } ) => { try { const response = await accountService.updateAccount( - id, firstName, lastName, email diff --git a/client/src/components/Button/AddNewCategory.jsx b/client/src/components/Button/AddNewCategory.jsx index 8e3f42cf..ea1cff8a 100644 --- a/client/src/components/Button/AddNewCategory.jsx +++ b/client/src/components/Button/AddNewCategory.jsx @@ -1,15 +1,13 @@ import React from "react"; import PropTypes from "prop-types"; -import { faPlus } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { MdAddCircle } from "react-icons/md"; import Button from "../Button/Button"; import { createUseStyles } from "react-jss"; const useStyles = createUseStyles(theme => ({ - iconContainer: { - backgroundColor: theme.colorPrimary, - borderRadius: "50%", - padding: "5px" + icon: { + color: theme.colorPrimary, + fontSize: "xx-large" }, buttonContainer: { display: "flex", @@ -36,9 +34,7 @@ const AddNewCategoryButton = ({ id, onClick }) => { data-testid={id} >
    -
    - -
    +
    {` Add New Category `}
    diff --git a/client/src/components/Button/DownloadButton.js b/client/src/components/Button/DownloadButton.js index 87aabc20..465eddf8 100644 --- a/client/src/components/Button/DownloadButton.js +++ b/client/src/components/Button/DownloadButton.js @@ -1,8 +1,7 @@ import React from "react"; import PropTypes from "prop-types"; import Button from "./Button"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faPrint } from "@fortawesome/free-solid-svg-icons"; +import { MdPrint } from "react-icons/md"; const DownloadButton = ({ id, onClick, isDisplayed }) => { return ( @@ -15,7 +14,7 @@ const DownloadButton = ({ id, onClick, isDisplayed }) => { data-testid={id} isDisplayed={isDisplayed} > - + Print Summary ); diff --git a/client/src/components/Button/EditToggleButton.js b/client/src/components/Button/EditToggleButton.js index af4b3821..d321846a 100644 --- a/client/src/components/Button/EditToggleButton.js +++ b/client/src/components/Button/EditToggleButton.js @@ -1,8 +1,7 @@ import React from "react"; import PropTypes from "prop-types"; -import { faEdit } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import Button from "../Button/Button"; +import { MdEdit } from "react-icons/md"; const EditToggleButton = ({ id, onClick, editMode }) => { return ( @@ -15,7 +14,7 @@ const EditToggleButton = ({ id, onClick, editMode }) => { style={{ margin: "0" }} > <> - + {editMode ? ` SAVE EDITS` : ` EDIT FAQ PAGE`} diff --git a/client/src/components/Button/NavButton.js b/client/src/components/Button/NavButton.js index 47d672f6..48de81b8 100644 --- a/client/src/components/Button/NavButton.js +++ b/client/src/components/Button/NavButton.js @@ -1,8 +1,7 @@ import React from "react"; import PropTypes from "prop-types"; import { createUseStyles, useTheme } from "react-jss"; -import { faAngleLeft, faAngleRight } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { MdChevronLeft, MdChevronRight } from "react-icons/md"; import clsx from "clsx"; import Button from "./Button"; @@ -11,7 +10,6 @@ const useStyles = createUseStyles({ cursor: "pointer", padding: "0.35em 0.7em", margin: "0.5em", - fontSize: "2em", border: "1px solid rgba(0, 0, 0, 0.1)", boxShadow: "rgba(0, 46, 109, 0.3) 0px 3px 5px", "&:focus": { @@ -53,9 +51,11 @@ const NavButton = ({ onClick={onClick} disabled={isDisabled} > - + {navDirection === "previous" ? ( + + ) : ( + + )} ); }; diff --git a/client/src/components/Button/PrintButton.js b/client/src/components/Button/PrintButton.js index 719948cf..429ff45b 100644 --- a/client/src/components/Button/PrintButton.js +++ b/client/src/components/Button/PrintButton.js @@ -1,8 +1,7 @@ import React from "react"; import PropTypes from "prop-types"; import Button from "./Button"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faPrint } from "@fortawesome/free-solid-svg-icons"; +import { MdPrint } from "react-icons/md"; const PrintButton = ({ id, onClick, isDisabled, isDisplayed }) => { return ( @@ -16,7 +15,7 @@ const PrintButton = ({ id, onClick, isDisabled, isDisplayed }) => { disabled={isDisabled} isDisplayed={isDisplayed} > - + Print Summary ); diff --git a/client/src/components/Checklist/ChecklistContent.js b/client/src/components/Checklist/ChecklistContent.js index 2c93144d..4c4e6eb3 100644 --- a/client/src/components/Checklist/ChecklistContent.js +++ b/client/src/components/Checklist/ChecklistContent.js @@ -1,7 +1,6 @@ import React from "react"; import { createUseStyles } from "react-jss"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons"; +import { MdLaunch } from "react-icons/md"; const useStyles = createUseStyles({ container: { @@ -114,10 +113,7 @@ const ChecklistContent = () => { > {" "} Draft Revised TDM Ordinance{" "} - + {" "} for applicability and exemption details.

    diff --git a/client/src/components/Checklist/ChecklistModal.js b/client/src/components/Checklist/ChecklistModal.js index 2e7ea291..fd7dc346 100644 --- a/client/src/components/Checklist/ChecklistModal.js +++ b/client/src/components/Checklist/ChecklistModal.js @@ -3,8 +3,7 @@ import { createUseStyles } from "react-jss"; import Modal from "react-modal"; import PropTypes from "prop-types"; import ChecklistContent from "./ChecklistContent"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faX } from "@fortawesome/free-solid-svg-icons"; +import { FaX } from "react-icons/fa6"; import "./ChecklistModal.css"; @@ -69,7 +68,8 @@ const ChecklistModal = ({ checklistModalOpen, toggleChecklistModal }) => { className={classes.modal} > - + {/* */} + diff --git a/client/src/components/Faq/CategoryInputContainer.jsx b/client/src/components/Faq/CategoryInputContainer.jsx index 9cbff0c1..af933653 100644 --- a/client/src/components/Faq/CategoryInputContainer.jsx +++ b/client/src/components/Faq/CategoryInputContainer.jsx @@ -1,11 +1,7 @@ import React from "react"; import PropTypes from "prop-types"; import { createUseStyles } from "react-jss"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - faGripHorizontal, - faTrashAlt -} from "@fortawesome/free-solid-svg-icons"; +import { MdDelete, MdViewModule } from "react-icons/md"; import { CategoryInput } from "./CategoryInput"; const useStyles = createUseStyles({ @@ -65,16 +61,12 @@ export const CategoryInputContainer = ({ {admin && ( <> {isHovered && ( - )} - + )} diff --git a/client/src/components/Faq/DeleteFaqModal.jsx b/client/src/components/Faq/DeleteFaqModal.jsx index 2d6bf05d..69f4a57c 100644 --- a/client/src/components/Faq/DeleteFaqModal.jsx +++ b/client/src/components/Faq/DeleteFaqModal.jsx @@ -4,8 +4,7 @@ import ModalDialog from "../UI/AriaModal/ModalDialog"; import Button from "../Button/Button"; import WarningIcon from "../../images/warning-icon.png"; import { createUseStyles } from "react-jss"; -import { faTrashCan } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { MdDelete } from "react-icons/md"; const useStyles = createUseStyles(theme => ({ buttonFlexBox: { @@ -36,7 +35,7 @@ const DeleteFaqModal = ({ isModalOpen, closeModal, handleDelete, isFaq }) => { return (
    - + {` Delete ${type}`}
    diff --git a/client/src/components/Faq/ExpandButtons.jsx b/client/src/components/Faq/ExpandButtons.jsx index e97439ee..8282fa7f 100644 --- a/client/src/components/Faq/ExpandButtons.jsx +++ b/client/src/components/Faq/ExpandButtons.jsx @@ -1,9 +1,7 @@ import React from "react"; import PropTypes from "prop-types"; import { createUseStyles } from "react-jss"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faAngleUp } from "@fortawesome/free-solid-svg-icons"; -import { faAngleDown } from "@fortawesome/free-solid-svg-icons"; +import { MdExpandLess, MdExpandMore } from "react-icons/md"; const useStyles = createUseStyles({ expandCollapseFlexContainer: { @@ -18,7 +16,7 @@ const useStyles = createUseStyles({ flexDirection: "column" }, faqCarotIcon: { - fontSize: "14px", + fontSize: "large", margin: "-2px" }, expandCollapseAll: { @@ -41,10 +39,7 @@ const ExpandButtons = ({ toggleExpandCollapse }) => {
    - +
    - +
    |
    diff --git a/client/src/components/Layout/Header.js b/client/src/components/Layout/Header.js index e135341b..ac579269 100644 --- a/client/src/components/Layout/Header.js +++ b/client/src/components/Layout/Header.js @@ -1,8 +1,7 @@ import React, { useState } from "react"; import { createUseStyles } from "react-jss"; import clsx from "clsx"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faBars } from "@fortawesome/free-solid-svg-icons"; +import { MdMenu } from "react-icons/md"; import logo from "../../images/ladot_white.png"; import NavBar from "./NavBar"; import { Environment } from "../../helpers/Environment"; @@ -99,8 +98,9 @@ const Header = () => { className={classes.hamburgerButton} onClick={handleHamburgerMenuClick} > - + +
    ); diff --git a/client/src/components/Layout/NavBarLogin.js b/client/src/components/Layout/NavBarLogin.js index 3103c828..099d796c 100644 --- a/client/src/components/Layout/NavBarLogin.js +++ b/client/src/components/Layout/NavBarLogin.js @@ -58,7 +58,7 @@ const NavBarLogin = ({ classes, handleHamburgerMenuClick }) => { state: { prevPath: location.pathname } }} onClick={() => { - userContext.updateAccount({}); + userContext.updateAccount(null); handleHamburgerMenuClick; }} > diff --git a/client/src/components/Layout/TdmAuthProvider.jsx b/client/src/components/Layout/TdmAuthProvider.jsx index d84fefea..6a1d3e2d 100644 --- a/client/src/components/Layout/TdmAuthProvider.jsx +++ b/client/src/components/Layout/TdmAuthProvider.jsx @@ -16,7 +16,7 @@ const getUserFromLocalStorage = () => { } }; -const TdmSecurity = ({ children }) => { +const TdmAuthProvider = ({ children }) => { const [account, setAccount] = useState(getUserFromLocalStorage()); const updateAccount = userAccount => { @@ -36,8 +36,8 @@ const TdmSecurity = ({ children }) => { ); }; -TdmSecurity.propTypes = { +TdmAuthProvider.propTypes = { children: PropTypes.any }; -export default TdmSecurity; +export default TdmAuthProvider; diff --git a/client/src/components/Offline.js b/client/src/components/Offline.js new file mode 100644 index 00000000..9c073994 --- /dev/null +++ b/client/src/components/Offline.js @@ -0,0 +1,58 @@ +import React from "react"; +import { createUseStyles } from "react-jss"; +import { MdError } from "react-icons/md"; +import wave from "../images/wave.svg"; + +const useStyles = createUseStyles(() => ({ + failure: { + color: "#E46247", + fontSize: "3.5em" + }, + offlineContainer: { + backgroundImage: `url(${wave})`, + boxSizing: "border-box", + overflow: "auto", + flexBasis: "auto", + flexGrow: "1", + flexShrink: "1", + alignItems: "center", + padding: "8em 2em 2em 2em", + flex: "1 1 auto", + display: "flex", + flexDirection: "column", + minHeight: "calc(100vh - 103px - 48px)", + backgroundRepeat: "no-repeat", + backgroundSize: "100vw", + backgroundPosition: "bottom" + }, + offlineText: { + paddingTop: "2em", + paddingBottom: "0.5em", + fontSize: "1.2em", + color: "#C35302", + fontWeight: "700" + } +})); + +const OfflinePage = () => { + // const location = useLocation(); + const classes = useStyles(); + + return ( +
    +
    + +
    +

    +
    + The Los Angeles TDM Calculator is unavailable at this time. +
    +

    +
    + Please email ladot.tdm@lacity.org for assistance +
    +
    + ); +}; + +export default OfflinePage; diff --git a/client/src/components/PdfPrint/PdfFooter.js b/client/src/components/PdfPrint/PdfFooter.js index 2477828d..83a34161 100644 --- a/client/src/components/PdfPrint/PdfFooter.js +++ b/client/src/components/PdfPrint/PdfFooter.js @@ -1,6 +1,9 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useContext } from "react"; import PropTypes from "prop-types"; import { createUseStyles } from "react-jss"; +import UserContext from "../../contexts/UserContext"; +import { DateTime } from "luxon"; +import { formatDatetime } from "../../helpers/util"; const useStyles = createUseStyles({ pdfTimeText: { @@ -8,23 +11,33 @@ const useStyles = createUseStyles({ color: "rgb(53,119,163)" }, pdfFooterContainer: { - margin: "24px 0 0" + margin: "24px 0 0", + width: "100%" } }); -const PdfFooter = props => { +const PdfFooter = ({ project }) => { + const { dateModified, dateSubmitted, dateSnapshotted, loginId } = project; const classes = useStyles(); - const { dateModified } = props; - const [printedDate, setPrintedDate] = useState(new Date()); + const userContext = useContext(UserContext); + const loggedInUserId = userContext.account?.id; + const formattedDateModified = formatDatetime(dateModified); + const formattedDateSnapshotted = formatDatetime(dateSnapshotted); + const formattedDateSubmitted = formatDatetime(dateSubmitted); + + const [formattedDatePrinted, setFormattedDatePrinted] = useState( + formatDatetime(DateTime.now()) + ); useEffect(() => { // You would update these dates based on your application logic // For instance, you might fetch them from an API when the component mounts const updateDates = () => { - setPrintedDate(new Date()); // replace with actual date + setFormattedDatePrinted(formatDatetime(DateTime.now())); // replace with actual date }; updateDates(); + const intervalId = setInterval(updateDates, 60 * 1000); // updates every minute return () => clearInterval(intervalId); // cleanup on unmount @@ -32,9 +45,34 @@ const PdfFooter = props => { return (
    -
    - Date Last Saved: {dateModified} Pacific Time -
    + {loginId && ( + <> +
    + Status:{" "} + {!formattedDateSnapshotted + ? "Draft" + : loginId === loggedInUserId + ? "Snapshot" + : "Shared Snapshot"} +
    + {formattedDateSubmitted && ( +
    + Snapshot Submitted: {formattedDateSubmitted} Pacific Time +
    + )} + {formattedDateSnapshotted && ( +
    + Snapshot Created: {formattedDateSnapshotted} Pacific Time +
    + )} + {formattedDateModified && ( +
    + Date Last Saved: {formattedDateModified} Pacific Time +
    + )} + + )} +
    { marginBottom: "5px" }} > - Date Printed:{" "} - {printedDate.toLocaleString("en-US", { - timeZone: "America/Los_Angeles" - })}{" "} - Pacific Time + Date Printed: {formattedDatePrinted}
    Los Angeles Department of Transportation | tdm.ladot.lacity.org | @@ -56,8 +90,9 @@ const PdfFooter = props => {
    ); }; + PdfFooter.propTypes = { - dateModified: PropTypes.string || null + project: PropTypes.shape }; export default PdfFooter; diff --git a/client/src/components/PdfPrint/PdfPrint.js b/client/src/components/PdfPrint/PdfPrint.js index b1eb5239..f1930ad6 100644 --- a/client/src/components/PdfPrint/PdfPrint.js +++ b/client/src/components/PdfPrint/PdfPrint.js @@ -36,30 +36,38 @@ const useStyles = createUseStyles({ marginTop: "25px" }, categoryHeaderContainer: { - background: "#E7EBF0", - padding: "12px" + paddingInline: "12px", + paddingBottom: "0", + display: "flex", + flexDirection: "row", + justifyContent: "left", + alignItems: "center", + gap: "2px" }, categoryHeader: { - fontSize: "16px", - fontFamily: "Oswald", - fontWeight: "700" + fontSize: "18px", + width: "100%", + color: "rgb(53,119,163)", + fontWeight: "900" }, pdfResultsContainer: { - display: "block", - padding: "10px 0", + flexDirection: "column", + padding: "10px 0.2em", maxWidth: "100%" }, measuresContainer: { paddingTop: "10px", - margin: "0 12px" + margin: "0 12px", + width: "90%" }, earnedPoints: { - fontFamily: "Oswald", - fontWeight: "500", - fontSize: "12px", - color: "#0F2940", + fontWeight: "600", + fontSize: "14px", + color: "#000000", paddingTop: "5px", - marginRight: "31px" + alignItems: "baseline", + width: "50%", + maxHeight: "20px" }, summaryContainer: { display: "flex", @@ -77,18 +85,39 @@ const useStyles = createUseStyles({ projectInfoDetailsContainer: { paddingTop: "20px", paddingLeft: "12px", + display: "grid", + gridTemplateColumns: "2fr 1fr", + gap: "1.1rem", + maxWidth: "100%", + minHeight: "55px" + }, + projectTitleName: { display: "flex", flexDirection: "row", - flexWrap: "wrap", - maxWidth: "100%", - minHeight: "55px", - rowGap: "1.1rem" + justifyContent: "space-between", + alignItems: "center", + gap: "2px", + marginLeft: "2px", + paddingInline: "50px" }, textProjectInfoHeaderAddress: { color: "rgba(15, 41, 64, .5)", fontSize: "16px", + padding: "0 0 0 8px", fontFamily: "Calibri", - fontWeight: 700 + fontWeight: 700, + fontStyle: "normal", + maxHeight: "20px", + alignItems: "baseline", + textIndent: "2px" + }, + projectDescription: { + fontSize: "14px" + }, + projectDescriptionValue: { + fontSize: "14px", + marginLeft: "12px", + border: "1px solid #E7EBF0" }, "@media (max-width: 768px)": { logoContainer: { @@ -100,7 +129,8 @@ const useStyles = createUseStyles({ // eslint-disable-next-line react/display-name export const PdfPrint = forwardRef((props, ref) => { const classes = useStyles(); - const { rules, dateModified } = props; + + const { rules, project } = props; const level = getRule(rules, "PROJECT_LEVEL"); const targetPoints = getRule(rules, "TARGET_POINTS_PARK"); @@ -156,8 +186,20 @@ export const PdfPrint = forwardRef((props, ref) => { {""} | TDM Calculation Project Summary
    -
    - PROJECT NAME +
    + + PROJECT NAME: + {projectName && projectName.value ? ( {projectName.value} @@ -166,7 +208,7 @@ export const PdfPrint = forwardRef((props, ref) => {
    {projectAddress && projectAddress.value && ( - + )} {parcelNumbers && parcelNumbers.value ? ( @@ -188,6 +230,21 @@ export const PdfPrint = forwardRef((props, ref) => { )}
    +
    +
    + RESULTS +
    +
    + + +
    +
    PROJECT DETAILS @@ -198,42 +255,44 @@ export const PdfPrint = forwardRef((props, ref) => { value={level.value.toString()} valueTestId={"summary-project-level-value"} /> -
    - - {rulesNotEmpty - ? specificationRules.map(rule => { - return ( - - ); - }) - : null} - - - - {projectDescription && - projectDescription.value && - projectDescription.value.length > 0 ? ( -
    -
    -
    {projectDescription.name}:
    -
    -
    - {projectDescription.value} + + {rulesNotEmpty + ? specificationRules.map(rule => { + return ( + + ); + }) + : null} + + + + {projectDescription && + projectDescription.value && + projectDescription.value.length > 0 ? ( +
    +
    +
    + {projectDescription.name}: +
    +
    +
    + {projectDescription.value} +
    -
    - ) : null} + ) : null} +
    @@ -256,38 +315,22 @@ export const PdfPrint = forwardRef((props, ref) => { User-Defined Strategy Details:
    -
    +
    {userDefinedStrategy.comment}
    ) : null} -
    -
    - RESULTS -
    -
    - - -
    -
    - + + ); }); + PdfPrint.propTypes = { rules: PropTypes.array, - account: PropTypes.object, - projectId: PropTypes.number, - loginId: PropTypes.number, - dateModified: PropTypes.string || null + project: PropTypes.shape }; export default PdfPrint; diff --git a/client/src/components/PdfPrint/PdfResult.js b/client/src/components/PdfPrint/PdfResult.js index e57cd4f5..498c6749 100644 --- a/client/src/components/PdfPrint/PdfResult.js +++ b/client/src/components/PdfPrint/PdfResult.js @@ -1,12 +1,23 @@ import React from "react"; import PropTypes from "prop-types"; +import { createUseStyles } from "react-jss"; + +const useStyles = createUseStyles({ + result: { + marginTop: "10px", + marginBottom: "-10px", + padding: "0px", + fontSize: "17px", + textIndent: "10px" + } +}); const PdfResult = props => { const { rule, valueTestId } = props; - + const classes = useStyles(); return rule ? (
    -

    +

    {rule.name}: {Math.round(rule.value)}

    diff --git a/client/src/components/ProjectWizard/InapplicableStrategiesModal.js b/client/src/components/ProjectWizard/InapplicableStrategiesModal.js index 1b5af989..f8e20024 100644 --- a/client/src/components/ProjectWizard/InapplicableStrategiesModal.js +++ b/client/src/components/ProjectWizard/InapplicableStrategiesModal.js @@ -3,8 +3,7 @@ import PropTypes from "prop-types"; import ModalDialog from "../UI/AriaModal/ModalDialog"; import Button from "../Button/Button"; import { createUseStyles } from "react-jss"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons"; +import { MdWarning } from "react-icons/md"; const useStyles = createUseStyles({ title: { @@ -34,11 +33,7 @@ const InapplicableStrategiesModal = props => { underlayClickExits={false} >
    - +

    Due to changes made to the project specifications, one or more TDM strategies are no longer applicable and have been automatically diff --git a/client/src/components/ProjectWizard/PackagePanel/PackageTooltips.js b/client/src/components/ProjectWizard/PackagePanel/PackageTooltips.js index 26b72fc9..709a7b29 100644 --- a/client/src/components/ProjectWizard/PackagePanel/PackageTooltips.js +++ b/client/src/components/ProjectWizard/PackagePanel/PackageTooltips.js @@ -1,7 +1,6 @@ import React from "react"; import { createUseStyles, useTheme } from "react-jss"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faCheck } from "@fortawesome/free-solid-svg-icons"; +import { MdCheck } from "react-icons/md"; const useStyles = createUseStyles({ box: { @@ -100,21 +99,21 @@ export const TooltipResidential = () => { - + Bike Parking + 2 Points - + Encouragement Program + 4 Points - + Unbundling Parking + 8 Points @@ -155,28 +154,28 @@ export const TooltipSchool = () => { - + Bike Parking + 2 Points - + Encouragement Program + 6 Points - + HOV Program + 2 Points - + School Safety Program + 4 Points diff --git a/client/src/components/ProjectWizard/Pagination.js b/client/src/components/ProjectWizard/Pagination.js deleted file mode 100644 index f51cda8d..00000000 --- a/client/src/components/ProjectWizard/Pagination.js +++ /dev/null @@ -1,88 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Link } from "react-router-dom"; -import { createUseStyles } from "react-jss"; -import clsx from "clsx"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - faChevronLeft, - faChevronRight -} from "@fortawesome/free-solid-svg-icons"; - -const useStyles = createUseStyles({ - paginationContainer: { - marginBottom: "20px" - }, - pagination: { - display: "flex" - }, - button: { - border: "none", - background: "none", - outline: "none", - width: "30px", - padding: "0", - margin: "0 5px" - }, - pageLinkContainer: { - height: "25px", - width: "25px", - display: "flex", - justifyContent: "center", - margin: "0 8px" - }, - pageLink: { - fontFamily: "Calibri", - fontWeight: "700", - margin: "auto 0", - fontSize: "18px" - } -}); - -const Pagination = props => { - const { projectsPerPage, totalProjects, paginate } = props; - const classes = useStyles(); - const pageNumbers = []; - - for (let i = 1; i <= Math.ceil(totalProjects / projectsPerPage); i++) { - pageNumbers.push(i); - } - - return ( -
    -
      - - {pageNumbers.map(number => ( -
    • - paginate(number)} - > - {number} - -
    • - ))} - -
    -
    - ); -}; - -export default Pagination; - -Pagination.propTypes = { - projectsPerPage: PropTypes.number.isRequired, - totalProjects: PropTypes.number.isRequired, - paginate: PropTypes.func.isRequired -}; diff --git a/client/src/components/ProjectWizard/RuleInput/RuleInputLabel.js b/client/src/components/ProjectWizard/RuleInput/RuleInputLabel.js index 400c9171..fbf57fb3 100644 --- a/client/src/components/ProjectWizard/RuleInput/RuleInputLabel.js +++ b/client/src/components/ProjectWizard/RuleInput/RuleInputLabel.js @@ -1,8 +1,7 @@ import React from "react"; import ToolTipLabel from "../../ToolTip/ToolTipLabel"; import PropTypes from "prop-types"; -import { faLink } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { MdLink } from "react-icons/md"; const RuleInputLabel = ({ id, @@ -29,7 +28,7 @@ const RuleInputLabel = ({ {link ? ( {name} - + ) : ( name diff --git a/client/src/components/ProjectWizard/TdmCalculationContainer.js b/client/src/components/ProjectWizard/TdmCalculationContainer.js index be22d66d..c6bbe88f 100644 --- a/client/src/components/ProjectWizard/TdmCalculationContainer.js +++ b/client/src/components/ProjectWizard/TdmCalculationContainer.js @@ -7,7 +7,7 @@ import * as ruleService from "../../services/rule.service"; import * as projectService from "../../services/project.service"; import Engine from "../../services/tdm-engine"; import { useToast } from "../../contexts/Toast"; -import moment from "moment"; +// import { formatDatetime } from "../../helpers/util"; // These are the calculation results we want to calculate // and display on the main page. @@ -45,13 +45,18 @@ export function TdmCalculationContainer({ contentContainerRef }) { const [engine, setEngine] = useState(null); const [formInputs, setFormInputs] = useState({}); const projectId = params.projectId ? Number(params.projectId) : 0; - const [loginId, setLoginId] = useState(0); const [strategiesInitialized, setStrategiesInitialized] = useState(false); const [formHasSaved, setFormHasSaved] = useState(true); const [inapplicableStrategiesModal, setInapplicableStrategiesModal] = useState(false); const [rules, setRules] = useState([]); - const [dateModified, setDateModified] = useState(); + + const [project, setProject] = useState({}); + // const [loginId, setLoginId] = useState(0); + // const [dateModified, setDateModified] = useState(); + // const [dateSnapshotted, setDateSnapshotted] = useState(); + // const [dateSubmitted, setDateSubmitted] = useState(); + const toast = useToast(); const fetchRules = useCallback(async () => { @@ -76,10 +81,15 @@ export function TdmCalculationContainer({ contentContainerRef }) { let inputs = {}; if (Number(projectId) > 0 && account.id) { projectResponse = await projectService.getById(projectId); - setLoginId(projectResponse.data.loginId); - setDateModified( - moment(projectResponse.data.dateModified).format("MM/DD/YYYY h:mm A") - ); + + // setLoginId(projectResponse.data.loginId); + // setDateModified(formatDatetime(projectResponse.data.dateModified)); + // setDateSnapshotted( + // formatDatetime(projectResponse.data?.dateSnapshotted) + // ); + // setDateSubmitted(formatDatetime(projectResponse.data?.dateSubmitted)); + setProject(projectResponse.data); + inputs = JSON.parse(projectResponse.data.formInputs); setStrategiesInitialized(true); } else { @@ -98,7 +108,7 @@ export function TdmCalculationContainer({ contentContainerRef }) { // const redirect = account.id ? "/projects" : "/login"; // navigate(redirect); } - }, [engine, projectId, account, setRules, setDateModified]); + }, [engine, projectId, account, setRules, setProject]); // Initialize the engine with saved project data, as appropriate. // Should run only when projectId changes. @@ -161,7 +171,9 @@ export function TdmCalculationContainer({ contentContainerRef }) { const strategyBike4 = rules.find(r => r.code === "STRATEGY_BIKE_4"); const strategyHov4 = rules.find(r => r.code === "STRATEGY_HOV_4"); const strategyInfo3 = rules.find(r => r.code === "STRATEGY_INFO_3"); - const strategyInfo5 = rules.find(r => r.code === "STRATEGY_INFO_5"); + const strategyMobilityInvestment2 = rules.find( + r => r.code === "STRATEGY_MOBILITY_INVESTMENT_2" + ); return ( strategyBike4 && !!strategyBike4.value && @@ -169,8 +181,8 @@ export function TdmCalculationContainer({ contentContainerRef }) { !!strategyHov4.value && strategyInfo3 && strategyInfo3.value >= 2 && - strategyInfo5 && - !!strategyInfo5.value + strategyMobilityInvestment2 && + strategyMobilityInvestment2.value >= 2 ); }; @@ -203,7 +215,7 @@ export function TdmCalculationContainer({ contentContainerRef }) { if (rules.find(r => r.code === "STRATEGY_INFO_3").value <= 1) { modifiedInputs["STRATEGY_INFO_3"] = 2; } - modifiedInputs["STRATEGY_INFO_5"] = true; + modifiedInputs["STRATEGY_MOBILITY_INVESTMENT_2"] = 2; modifiedInputs["STRATEGY_HOV_4"] = true; // De-select Trip-Reduction Program @@ -217,7 +229,7 @@ export function TdmCalculationContainer({ contentContainerRef }) { } else { modifiedInputs["STRATEGY_INFO_3"] = 1; } - modifiedInputs["STRATEGY_INFO_5"] = false; + modifiedInputs["STRATEGY_MOBILITY_INVESTMENT_2"] = 0; modifiedInputs["STRATEGY_HOV_4"] = false; } } @@ -445,10 +457,11 @@ export function TdmCalculationContainer({ contentContainerRef }) { setFormHasSaved(true); toast.add("Saved Project Changes"); let projectResponse = null; + projectResponse = await projectService.getById(projectId); - setDateModified( - moment(projectResponse.data.dateModified).format("MM/DD/YYYY h:mm A") - ); + + // setDateModified(formatDatetime(projectResponse.data?.dateModified)); + setProject(projectResponse.data); } catch (err) { console.error(err); if (err.response) { @@ -472,7 +485,8 @@ export function TdmCalculationContainer({ contentContainerRef }) { const postResponse = await projectService.post(requestBody); // Update URL to /calculation// // to keep working on same project. - const newPath = `/calculation/${location.pathname.split("/")[1]}/${ + + const newPath = `/calculation/${location.pathname.split("/")[2]}/${ postResponse.data.id }`; navigate(newPath, { replace: true }); @@ -512,7 +526,6 @@ export function TdmCalculationContainer({ contentContainerRef }) { onPkgSelect={onPkgSelect} onParkingProvidedChange={onParkingProvidedChange} resultRuleCodes={resultRuleCodes} - loginId={loginId} onSave={onSave} allowResidentialPackage={allowResidentialPackage} allowSchoolPackage={allowSchoolPackage} @@ -520,10 +533,15 @@ export function TdmCalculationContainer({ contentContainerRef }) { schoolPackageSelected={schoolPackageSelected} formIsDirty={!formHasSaved} projectIsValid={projectIsValid} - dateModified={dateModified} + // loginId={loginId} + // dateModified={dateModified} + // dateSnapshotted={dateSnapshotted} + // dateSubmitted={dateSubmitted} contentContainerRef={contentContainerRef} inapplicableStrategiesModal={inapplicableStrategiesModal} closeStrategiesModal={closeStrategiesModal} + // projectId={projectId} + project={project} /> ); } diff --git a/client/src/components/ProjectWizard/TdmCalculationWizard.js b/client/src/components/ProjectWizard/TdmCalculationWizard.js index 3cd5ce6d..75ca82ee 100644 --- a/client/src/components/ProjectWizard/TdmCalculationWizard.js +++ b/client/src/components/ProjectWizard/TdmCalculationWizard.js @@ -45,7 +45,7 @@ const TdmCalculationWizard = props => { onPkgSelect, onParkingProvidedChange, resultRuleCodes, - loginId, + // loginId, onSave, allowResidentialPackage, allowSchoolPackage, @@ -53,10 +53,13 @@ const TdmCalculationWizard = props => { schoolPackageSelected, formIsDirty, projectIsValid, - dateModified, + // dateModified, + // dateSnapshotted, + // dateSubmitted, contentContainerRef, inapplicableStrategiesModal, - closeStrategiesModal + closeStrategiesModal, + project } = props; const classes = useStyles(); const context = useContext(ToastContext); @@ -68,39 +71,43 @@ const TdmCalculationWizard = props => { const projectId = Number(params.projectId); const { pathname } = useLocation(); const [ainInputError, setAINInputError] = useState(""); + const loginId = project.loginId; /* shouldBlock determines if user should be blocked from navigating away from wizard. Note that navigation from /calculation/a/x to /calculation/b/x is just going to a different step of the wizard, and is allowed. */ const calculationPath = "/calculation/:page/:projectId?/*"; - const isSameProject = (currentLocation, nextLocation) => { - const currentMatch = matchPath( - { - path: calculationPath, - exact: true - }, - currentLocation.pathname - ); - const nextMatch = matchPath( - { - path: calculationPath, - exact: true - }, - nextLocation.pathname - ); - return ( - currentMatch && - nextMatch && - currentMatch.params.projectId === nextMatch.params.projectId - ); - }; const shouldBlock = React.useCallback( ({ currentLocation, nextLocation }) => { + const isSameProject = (currentLocation, nextLocation) => { + const currentMatch = matchPath( + { + path: calculationPath, + exact: true + }, + currentLocation.pathname + ); + const nextMatch = matchPath( + { + path: calculationPath, + exact: true + }, + nextLocation.pathname + ); + + return ( + currentMatch && + nextMatch && + (currentMatch.params.projectId === nextMatch.params.projectId || + !projectId) + ); + }; + return formIsDirty && !isSameProject(currentLocation, nextLocation); }, - [formIsDirty] + [formIsDirty, projectId] ); const blocker = useBlocker(shouldBlock); @@ -168,7 +175,7 @@ const TdmCalculationWizard = props => { }; const setDisabledSaveButton = () => { - const loggedIn = account && !!account.id; + const loggedIn = !!account && !!account.id; const notASavedProject = !projectId; const projectBelongsToUser = account && account.id === loginId; const setDisabled = !( @@ -181,7 +188,7 @@ const TdmCalculationWizard = props => { }; const setDisplaySaveButton = () => { - const loggedIn = account && !!account.id; + const loggedIn = !!account && !!account.id; const setDisplayed = loggedIn; return setDisplayed; }; @@ -286,14 +293,13 @@ const TdmCalculationWizard = props => { projectId={projectId} loginId={loginId} onSave={onSave} - dateModified={dateModified} + dateModified={project.dateModified} /> ); default: return null; } }; - return (
    { > {pageContents(page)} { setDisplaySaveButton={setDisplaySaveButton} setDisplayPrintButton={setDisplayPrintButton} onSave={onSave} - dateModified={dateModified} + project={project} + // dateModified={dateModified} + // dateSnapshotted={dateSnapshotted} + // dateSubmitted={dateSubmitted} + // loginId={loginId} />
    @@ -360,7 +371,7 @@ TdmCalculationWizard.propTypes = { onResetProject: PropTypes.func.isRequired, filters: PropTypes.object.isRequired, resultRuleCodes: PropTypes.array.isRequired, - loginId: PropTypes.number.isRequired, + // loginId: PropTypes.number.isRequired, onSave: PropTypes.func.isRequired, allowResidentialPackage: PropTypes.bool.isRequired, allowSchoolPackage: PropTypes.bool.isRequired, @@ -368,9 +379,12 @@ TdmCalculationWizard.propTypes = { schoolPackageSelected: PropTypes.func, formIsDirty: PropTypes.bool, projectIsValid: PropTypes.func, - dateModified: PropTypes.string, + // dateModified: PropTypes.string, + // dateSnapshotted: PropTypes.string, + // dateSubmitted: PropTypes.string, inapplicableStrategiesModal: PropTypes.bool, - closeStrategiesModal: PropTypes.func + closeStrategiesModal: PropTypes.func, + project: PropTypes.shape }; export default TdmCalculationWizard; diff --git a/client/src/components/ProjectWizard/WizardFooter.js b/client/src/components/ProjectWizard/WizardFooter.js index c1ff21e8..eb4c049c 100644 --- a/client/src/components/ProjectWizard/WizardFooter.js +++ b/client/src/components/ProjectWizard/WizardFooter.js @@ -1,4 +1,4 @@ -import React, { useRef } from "react"; +import React, { useRef, useContext } from "react"; import PropTypes from "prop-types"; import NavButton from "../Button/NavButton"; import SaveButton from "../Button/SaveButton"; @@ -6,6 +6,8 @@ import { createUseStyles } from "react-jss"; import PrintButton from "../Button/PrintButton"; import ReactToPrint from "react-to-print"; import { PdfPrint } from "../PdfPrint/PdfPrint"; +import { formatDatetime } from "../../helpers/util"; +import UserContext from "../../contexts/UserContext"; const useStyles = createUseStyles({ allButtonsWrapper: { @@ -27,6 +29,13 @@ const useStyles = createUseStyles({ }, lastSavedContainer: { margin: "0 auto" + }, + datesStatus: { + width: "90%", + display: "flex", + alignItems: "flex-start", + flexDirection: "column", + gap: "7px" } }); @@ -40,7 +49,7 @@ const WizardFooter = ({ setDisplaySaveButton, setDisplayPrintButton, onSave, - dateModified + project }) => { const classes = useStyles(); const componentRef = useRef(); @@ -48,6 +57,11 @@ const WizardFooter = ({ const projectName = projectNameRule ? projectNameRule.value : "TDM Calculation Summary"; + const formattedDateSnapshotted = formatDatetime(project.dateSnapshotted); + const formattedDateSubmitted = formatDatetime(project.dateSubmitted); + const formattedDateModified = formatDatetime(project.dateModified); + const userContext = useContext(UserContext); + const loggedInUserId = userContext.account?.id; return ( <> @@ -92,11 +106,7 @@ const WizardFooter = ({ pageStyle=".printContainer {overflow: hidden;}" />
    - +
    ) : null}

    + + {page === 5 && formattedDateModified && loggedInUserId ? ( +
    +
    + Status: + {!formattedDateSnapshotted + ? "Draft" + : project.loginId === loggedInUserId + ? "Snapshot" + : "Shared Snapshot"} +
    + {formattedDateSubmitted ? ( +
    + Snapshot Submitted: + {formattedDateSubmitted} Pacific Time +
    + ) : null} + {formattedDateSnapshotted ? ( +
    + Snapshot Created: + {formattedDateSnapshotted} Pacific Time +
    + ) : null} +
    + Date Last Saved: + {formattedDateModified} Pacific Time +
    +
    + ) : ( + "" + )} ); }; -// TODO: WizardFooter.propTypes = { classes: PropTypes.any, rules: PropTypes.any, @@ -125,7 +165,7 @@ WizardFooter.propTypes = { setDisplayPrintButton: PropTypes.any, onSave: PropTypes.any, onDownload: PropTypes.any, - dateModified: PropTypes.any + project: PropTypes.shape }; export default WizardFooter; diff --git a/client/src/components/ProjectWizard/WizardPages/Level0Page.jsx b/client/src/components/ProjectWizard/WizardPages/Level0Page.jsx index 92be2f98..441e1b0b 100644 --- a/client/src/components/ProjectWizard/WizardPages/Level0Page.jsx +++ b/client/src/components/ProjectWizard/WizardPages/Level0Page.jsx @@ -3,8 +3,7 @@ import PropTypes from "prop-types"; import PlanningIcon from "../../../images/planning.png"; import WarningIcon from "../../../images/warning-icon.png"; import { createUseStyles } from "react-jss"; -import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { MdLaunch } from "react-icons/md"; const useStyles = createUseStyles({ level0NavButtons: { @@ -69,10 +68,7 @@ const Level0Page = ({ isLevel0 }) => { target="external" > Draft Revised TDM Ordinance{" "} - + {" "} for applicability and exemption details.

    diff --git a/client/src/components/ProjectWizard/WizardPages/ProjectMeasures.js b/client/src/components/ProjectWizard/WizardPages/ProjectMeasures.js index df4504a9..f2452d57 100644 --- a/client/src/components/ProjectWizard/WizardPages/ProjectMeasures.js +++ b/client/src/components/ProjectWizard/WizardPages/ProjectMeasures.js @@ -4,8 +4,7 @@ import PackagePanel from "../PackagePanel/PackagePanel"; import RuleStrategyPanels from "../RuleStrategy/RuleStrategyPanels"; import { createUseStyles, useTheme } from "react-jss"; import ResetButtons from "./ResetButtons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faCheckCircle } from "@fortawesome/free-solid-svg-icons"; +import { MdCheckCircle } from "react-icons/md"; const useStyles = createUseStyles({ pkgSelectContainer: { @@ -81,10 +80,7 @@ function ProjectMeasure(props) { {(allowResidentialPackage || allowSchoolPackage) && ( <>
    - +
    You qualify for a bonus package to earn 1 extra point!
    diff --git a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/LandUses.jsx b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/LandUses.jsx index 5aa39e20..bcdea5f5 100644 --- a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/LandUses.jsx +++ b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/LandUses.jsx @@ -11,21 +11,14 @@ const useStyles = createUseStyles({ margin: "4px auto" }, ruleName: { - minWidth: "270px" + minWidth: "270px", + fontSize: "14px" }, - pointsContainer: { + value: { display: "flex", - justifyContent: "flex-end" - }, - measureDetails: { - fontSize: "14px", - textAlign: "right", - minWidth: "40px", - marginRight: "10px" - }, - measureUnits: { fontSize: "14px", - width: "65px" + justifyContent: "flex-end", + marginRight: "6.5rem" } }); @@ -35,7 +28,8 @@ const LandUses = props => { return (
    -
    +
    Land Uses
    +
    {rules .filter( rule => rule.used && rule.value && rule.calculationPanelId === 5 @@ -43,10 +37,6 @@ const LandUses = props => { .map(r => r.name) .join(", ")}
    -
    -
    -
    -
    ); }; diff --git a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/MeasureSelected.jsx b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/MeasureSelected.jsx index 97c507b8..324f7fa3 100644 --- a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/MeasureSelected.jsx +++ b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/MeasureSelected.jsx @@ -1,11 +1,11 @@ import React from "react"; import PropTypes from "prop-types"; import { createUseStyles } from "react-jss"; -import clsx from "clsx"; import { roundToTwo } from "../../helpers"; const useStyles = createUseStyles({ rule: { + fontSize: "0.875rem", display: "flex", alignItems: "center", justifyContent: "space-between", @@ -13,30 +13,24 @@ const useStyles = createUseStyles({ margin: "4px auto" }, ruleName: { - minWidth: "270px" + flexBasis: "40%" }, ruleText: { - fontSize: "14px", - textAlign: "center", + flexBasis: "40%", + textAlign: "right", margin: "0 16px" }, - detailsContainer: { - display: "flex", - minWidth: "180px", - maxWidth: "35%" - }, pointsContainer: { display: "flex", justifyContent: "flex-end" }, value: { - fontFamily: "Oswald", - fontSize: "18px", - fontWeight: "700", + fontWeight: "600", textAlign: "right" }, calcUnitsPts: { - margin: "3px 45px 0 10px" + marginLeft: "0.5rem", + minWidth: "6rem" } }); @@ -47,20 +41,19 @@ const MeasureSelected = props => { return (
    {rule.name}
    -
    -
    - {rule.dataType === "boolean" || rule.dataType === "number" - ? null - : rule.dataType === "choice" + +
    + {rule.dataType === "boolean" || rule.dataType === "number" + ? null + : rule.dataType === "choice" + ? rule.choices.find( + choice => Number(choice.id) === Number(rule.value) + ) ? rule.choices.find( choice => Number(choice.id) === Number(rule.value) - ) - ? rule.choices.find( - choice => Number(choice.id) === Number(rule.value) - ).name - : rule.value - : rule.value} -
    + ).name + : rule.value + : rule.value}
    {roundToTwo(rule.calcValue)}
    diff --git a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/PointsEarnedMessage.jsx b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/PointsEarnedMessage.jsx index 8a40df30..7e948c5a 100644 --- a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/PointsEarnedMessage.jsx +++ b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/PointsEarnedMessage.jsx @@ -2,11 +2,7 @@ import React from "react"; import PropTypes from "prop-types"; import { createUseStyles } from "react-jss"; import clsx from "clsx"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - faCheckCircle, - faExclamationTriangle -} from "@fortawesome/free-solid-svg-icons"; +import { MdCheckCircle, MdWarning } from "react-icons/md"; const useStyles = createUseStyles({ success: { @@ -53,7 +49,7 @@ const PointsEarnedMessage = props => { {targetPointsReached ? (
    - {" "} + {" "}
    You have successfully earned the target points.
    @@ -62,10 +58,7 @@ const PointsEarnedMessage = props => { ) : (
    - +
    You have not reached the target points.
    Please, go back and review your strategies diff --git a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectDetail.jsx b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectDetail.jsx index 13050063..b7b7db9f 100644 --- a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectDetail.jsx +++ b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectDetail.jsx @@ -10,26 +10,25 @@ const useStyles = createUseStyles({ alignItems: "center", justifyContent: "space-between", minHeight: "24px", - margin: "4px auto" + margin: "4px auto", + fontSize: "0.875rem" }, ruleName: { - minWidth: "270px" + flexBasis: "80%" }, pointsContainer: { display: "flex", - justifyContent: "flex-end" + justifyContent: "flex-end", + flaxBasis: "20%" }, measureDetails: { - fontFamily: "Oswald", - fontSize: "18px", textAlign: "right", - fontWeight: "700", + fontWeight: "600", minWidth: "40px", marginRight: "10px" }, measureUnits: { - fontSize: "14px", - width: "65px" + minWidth: "6rem" }, leftIndent: { marginLeft: "20px" diff --git a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectInfo.jsx b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectInfo.jsx index 6c0d8623..771ee240 100644 --- a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectInfo.jsx +++ b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectInfo.jsx @@ -7,21 +7,19 @@ const useStyles = createUseStyles({ display: "flex", alignItems: "baseline", maxHeight: "20px", - width: "50%" + width: "50%", + fontSize: "0.875rem" }, projectInfoCategory: { - fontFamily: "Oswald", - fontWeight: "500", - fontSize: "12px", + fontWeight: "600", textTransform: "uppercase", - color: "rgba(15, 41, 64, .5)", - marginRight: "17px" + color: "rgba(0, 0, 0, 1)", + marginRight: "2px" }, projectInfoDetails: { - color: "#0F2940", + color: "rgba(0, 5, 30, 1)", fontFamily: "Calibri", - fontWeight: 700, - fontSize: "16px" + fontWeight: 400 } }); @@ -31,7 +29,7 @@ const ProjectInfo = props => { return (
    - {name} + {name + ":"} {rule && rule.value ? ( {rule.value} ) : null} diff --git a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectInfoContainer.jsx b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectInfoContainer.jsx index 3a08eb4d..129049d0 100644 --- a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectInfoContainer.jsx +++ b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectInfoContainer.jsx @@ -12,7 +12,7 @@ const useStyles = createUseStyles({ minHeight: "100px" }, textProjectInfoHeader: { - color: "#0F2940", + color: "#000000", fontSize: "24px", fontFamily: "Calibri", fontWeight: 700, @@ -20,7 +20,7 @@ const useStyles = createUseStyles({ paddingRight: ".8em" }, textProjectInfoHeaderAddress: { - color: "rgba(15, 41, 64, .5)", + color: "rgba(0, 0, 0, 1)", fontSize: "24px", fontFamily: "Calibri", fontWeight: "700" @@ -59,7 +59,7 @@ const ProjectInfoContainer = props => {
    {projectAddress && ( - + )} {buildingPermit && ( diff --git a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectInfoList.jsx b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectInfoList.jsx index a3151e20..beae0095 100644 --- a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectInfoList.jsx +++ b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectInfoList.jsx @@ -6,21 +6,20 @@ const useStyles = createUseStyles({ projectInfoDetailsSubContainer: { display: "flex", alignItems: "baseline", - width: "50%" + maxHeight: "20px" }, projectInfoCategory: { - fontFamily: "Oswald", - fontWeight: "500", - fontSize: "12px", + fontWeight: "600", + fontSize: "14px", textTransform: "uppercase", - color: "rgba(15, 41, 64, .5)", - marginRight: "17px" + color: "rgba(0, 0, 0, 1)", + marginRight: "2px" }, projectInfoDetails: { - color: "#0F2940", + color: "#00051e", fontFamily: "Calibri", - fontWeight: 700, - fontSize: "16px" + fontWeight: 600, + fontSize: "14px" }, AINValuesContainer: { display: "flex", @@ -38,11 +37,11 @@ const ProjectInfoList = props => { const classes = useStyles(); const { name, rule } = props; - const values = rule.value.split(","); + const values = rule.value ? rule.value.split(",") : []; return (
    - {name} + {name + ":"} {rule && rule.value ? ( diff --git a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectSummary.js b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectSummary.js index 5004d9aa..a5c9819c 100644 --- a/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectSummary.js +++ b/client/src/components/ProjectWizard/WizardPages/ProjectSummary/ProjectSummary.js @@ -1,9 +1,6 @@ import React from "react"; import PropTypes from "prop-types"; -import { createUseStyles } from "react-jss"; import clsx from "clsx"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faClock } from "@fortawesome/free-solid-svg-icons"; import Loader from "react-loader"; import { numberWithCommas, getRule, roundToTwo } from "../../helpers"; import ProjectInfoContainer from "./ProjectInfoContainer"; @@ -12,7 +9,7 @@ import MeasureSelected from "./MeasureSelected"; import PointsEarnedMessage from "./PointsEarnedMessage"; import LandUses from "./LandUses"; import Result from "./Result"; -import { useTheme } from "react-jss"; +import { createUseStyles, useTheme } from "react-jss"; const useStyles = createUseStyles({ projectSummary: { @@ -20,6 +17,13 @@ const useStyles = createUseStyles({ flexDirection: "column", flex: "1 1 auto" }, + reviewPage: { + display: "flex", + flex: "1 1 auto", + flexDirection: "column", + maxWidth: "600px", + minWidth: "60vw" + }, // success: { // color: "#A7C539" // }, @@ -43,6 +47,7 @@ const useStyles = createUseStyles({ margin: "4px auto" }, ruleName: { + fontSize: "14px", minWidth: "270px" }, loaderContainer: { @@ -64,6 +69,7 @@ const useStyles = createUseStyles({ categoryHeaderContainer: { background: "#E7EBF0", paddingLeft: "12px", + paddingRight: "3rem", paddingTop: "4px" }, categoryHeader: { @@ -71,7 +77,8 @@ const useStyles = createUseStyles({ fontFamily: "Calibri", fontWeight: "700", fontStyle: "normal", - lineHeight: "32px" + lineHeight: "32px", + background: "#E7EBF0" // fontSmoothing: "antialiased" }, resultsContainer: { @@ -88,12 +95,15 @@ const useStyles = createUseStyles({ earnedPoints: { fontFamily: "Calibri", fontWeight: "500", - fontSize: "12px", - color: "#0F2940", - paddingTop: "5px", - marginRight: "31px" + fontSize: "14px", + marginTop: "auto", + marginBottom: "auto", + color: "#00000", + verticalAlign: "center", + minWidth: "6rem" }, summaryContainer: { + fontSize: "14px", display: "flex", minWidth: "180px", maxWidth: "100%", @@ -178,18 +188,10 @@ const ProjectSummary = props => { : classes.failureBorder; return ( -
    +

    TDM Calculation Summary

    -
    - {props.dateModified && ( - -  Last saved:{" "} - {props.dateModified} - - )} -
    {!loading ? ( <> @@ -217,39 +219,6 @@ const ProjectSummary = props => {
    {rules ? : null} - -
    -
    - - TDM STRATEGIES SELECTED - - EARNED POINTS -
    -
    - {rulesNotEmpty - ? measureRules.map(rule => ( - - )) - : null} - {userDefinedStrategy.calcValue && - userDefinedStrategy.comment.length > 0 ? ( -
    -
    -
    - User-Defined Strategy Details: -
    -
    -
    - {userDefinedStrategy.comment} -
    -
    - ) : null} -
    -
    {
    ) : null}
    -
    - - The ordinances behind this TDM calculator are still in public - comment. No submission is possible at this time. +
    +
    +
    + + TDM STRATEGIES SELECTED + EARNED POINTS
    +
    + {rulesNotEmpty + ? measureRules.map(rule => ( + + )) + : null} + {userDefinedStrategy.calcValue && + userDefinedStrategy.comment.length > 0 ? ( +
    +
    +
    + User-Defined Strategy Details: +
    +
    +
    + {userDefinedStrategy.comment} +
    +
    + ) : null} +
    +
    +
    + + The ordinances behind this TDM calculator are still in public + comment. No submission is possible at this time. +
    ) : ( diff --git a/client/src/components/ProjectWizard/WizardSidebar/SidebarCart.js b/client/src/components/ProjectWizard/WizardSidebar/SidebarCart.js index afe2881c..60e4468d 100644 --- a/client/src/components/ProjectWizard/WizardSidebar/SidebarCart.js +++ b/client/src/components/ProjectWizard/WizardSidebar/SidebarCart.js @@ -34,7 +34,10 @@ const useStyles = createUseStyles({ dataPointsCell: { textAlign: "right" }, dataPoints: { fontSize: "18px", fontFamily: "Oswald" }, dataUnits: { fontSize: "14px", fontFamily: "Calibri", marginBottom: "10px" }, - row: { display: "flex", justifyContent: "space-between" }, + row: { + display: "flex", + justifyContent: "space-between" + }, noDisplay: { display: "none !important" }, diff --git a/client/src/components/ProjectWizard/WizardSidebar/SidebarPoints.js b/client/src/components/ProjectWizard/WizardSidebar/SidebarPoints.js index f78345da..c2d83ae4 100644 --- a/client/src/components/ProjectWizard/WizardSidebar/SidebarPoints.js +++ b/client/src/components/ProjectWizard/WizardSidebar/SidebarPoints.js @@ -49,6 +49,18 @@ const useStyles = createUseStyles({ visibility: "visible !important", opacity: "1 !important" } + }, + metricsPanelItem: { + display: "flex", + flexDirection: "column", + alignItems: "center", + flexBasis: "25%", + flexGrow: 0, + flexShrink: 1, + margin: 0, + padding: "0.5em", + backgroundColor: "transparent", + color: "white" } }); @@ -76,7 +88,7 @@ const SidebarPoints = props => { : classes.ruleEarnedOrange; return ( -
    +
    {rule.value}
    diff --git a/client/src/components/ProjectWizard/WizardSidebar/SidebarPointsPanel.js b/client/src/components/ProjectWizard/WizardSidebar/SidebarPointsPanel.js index 94e1ecf1..6cf87a67 100644 --- a/client/src/components/ProjectWizard/WizardSidebar/SidebarPointsPanel.js +++ b/client/src/components/ProjectWizard/WizardSidebar/SidebarPointsPanel.js @@ -6,10 +6,47 @@ import SidebarPoints from "./SidebarPoints"; import EarnedPointsProgress from "./EarnedPointsProgress"; import SidebarCart from "./SidebarCart"; import ToolTip from "../../ToolTip/ToolTip"; +import { createUseStyles } from "react-jss"; + +const useStyles = createUseStyles({ + resultsPanel: { + display: "flex", + flexDirection: "row-reverse", + backgroundColor: "transparent", + color: "white", + flex: "0 1 25%", + alignItems: "center", + justifyContent: "center" + }, + divider: { + border: "none", + borderTop: "3px dotted #e7ebf0", + width: "100%" + }, + calculationProgress: { + flex: "0 1 25%", + display: "flex", + justifyContent: "center" + }, + calculationCart: { + flex: "1 0 50%", + display: "flex", + flexDirection: "column", + alignItems: "stretch", + justifyContent: "flex-start", + backgroundColor: "#f7f9fa", + fontFamily: "Oswald", + padding: "10px", + lineHeight: "1.5em", + color: "#0f2940", + overflowY: "scroll" + } +}); const USE_PROGRESS_DIAL = true; const SidebarPointsPanel = props => { + const classes = useStyles(); const { rules, strategyRules, page } = props; let targetPointsRule = {}; let earnedPointsRule = {}; @@ -32,7 +69,7 @@ const SidebarPointsPanel = props => { return ( -
    +
    { rules={rules} />
    -
    +
    {USE_PROGRESS_DIAL ? ( -
    +
    {
    ) : ( <> -
    +
    { ? {} : { visibility: "hidden" } } - className="tdm-calculation-cart" + className={classes.calculationCart} >
    diff --git a/client/src/components/ProjectWizard/WizardSidebar/WizardSidebar.js b/client/src/components/ProjectWizard/WizardSidebar/WizardSidebar.js index fdca112f..694f3173 100644 --- a/client/src/components/ProjectWizard/WizardSidebar/WizardSidebar.js +++ b/client/src/components/ProjectWizard/WizardSidebar/WizardSidebar.js @@ -10,10 +10,10 @@ const useStyles = createUseStyles({ display: "flex", position: "sticky", top: 0, - // height: "calc(100vh - 103px - 48px)", height: "calc(100vh)", flexDirection: "column", - "@media (max-width:768px)": { + "@media (max-height: 800px)": { + overflowY: "hidden", height: "auto" } } diff --git a/client/src/components/Projects/CopyProjectModal.js b/client/src/components/Projects/CopyProjectModal.js index e1bdcbea..2bbc4544 100644 --- a/client/src/components/Projects/CopyProjectModal.js +++ b/client/src/components/Projects/CopyProjectModal.js @@ -1,11 +1,8 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import PropTypes from "prop-types"; import { createUseStyles, useTheme } from "react-jss"; - import Button from "../Button/Button"; -import { faCopy } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - +import { MdFileCopy } from "react-icons/md"; import ModalDialog from "../UI/AriaModal/ModalDialog"; const useStyles = createUseStyles(theme => ({ @@ -29,6 +26,10 @@ export default function CopyProjectModal({ `${selectedProjectName} (COPY)` ); + useEffect(() => { + setDuplicateProjectName(`${selectedProjectName} (COPY)`); + }, [selectedProjectName]); + return (
    - Duplicate Project + Duplicate Project
    Type a new name to duplicate the project diff --git a/client/src/components/Projects/CsvModal.js b/client/src/components/Projects/CsvModal.js new file mode 100644 index 00000000..9a8908df --- /dev/null +++ b/client/src/components/Projects/CsvModal.js @@ -0,0 +1,219 @@ +import React, { useState } from "react"; +import { PropTypes } from "prop-types"; +import { FaFileCsv } from "react-icons/fa"; +import Button from "../Button/Button"; +import { createUseStyles, useTheme } from "react-jss"; +import ModalDialog from "../UI/AriaModal/ModalDialog"; +import { CSVLink } from "react-csv"; +import { getCsvForProjects } from "./csvData"; +import RadioButton from "../UI/RadioButton"; + +const useStyles = createUseStyles(theme => ({ + buttonFlexBox: { + display: "flex", + flexDirection: "row", + justifyContent: "center", + margin: 0 + }, + heading1: theme.typography.heading1, + instruction: { + fontSize: "20px", + lineHeight: "32px", + textAlign: "center", + color: "#B64E38", + "& span": { + fontStyle: "italic" + } + }, + warningIcon: { + margin: "0 10px" + } +})); + +const CsvModal = ({ + mounted, + onClose, + project, + projects, + filteredProjects, + checkedProjects +}) => { + const theme = useTheme(); + const classes = useStyles(); + const [csvData, setCsvData] = useState(null); + const [projectCollection, setProjectCollection] = useState(""); + const [loading, setLoading] = useState(false); + const [filename, setFilename] = useState(""); + const [progress, setProgress] = useState(0); + + if (filename === "") { + setFilename(new Date().toISOString()); + } + + const handleOptionChange = changeEvent => { + setProjectCollection(changeEvent.target.value); + }; + + const handleFilenameChange = changeEvent => { + setFilename(changeEvent.target.value); + }; + + const handleGenerateButton = async () => { + setLoading(true); + const projectSet = project + ? [project] + : projectCollection === "All" + ? projects + : projectCollection == "Filtered" + ? filteredProjects + : checkedProjects; + let csvData = null; + csvData = await getCsvForProjects(projectSet, progressCallback); + setLoading(false); + setCsvData(csvData); + }; + + const progressCallback = percent => { + setProgress(percent); + }; + + return ( + { + onClose(); + setCsvData(null); + setProjectCollection(""); + setFilename(""); + }} + // initialFocus="#cancelButton" + > +
    + Generate CSV File +
    + {project ? ( +
    + {"Create a CSV for the project " + project.name} +
    + ) : ( + <> +
    + Choose a set of Projects to include +
    +
    + {checkedProjects && checkedProjects.length > 0 && ( +
    + +
    + )} + +
    + +
    +
    + +
    +
    + + )} + +
    + Specify file name for downloaded file +
    +
    + +
    + {loading && ( + + )} +
    +
    + + + {(project || projectCollection) && !loading && !csvData && ( + + )} + + {(project || projectCollection) && !loading && csvData ? ( + { + setCsvData(null); + setProjectCollection(""); + onClose(); + setFilename(""); + }} + > + DOWNLOAD FILE + + ) : null} +
    +
    +
    + ); +}; + +CsvModal.propTypes = { + mounted: PropTypes.bool, + onClose: PropTypes.func, + project: PropTypes.any, + projects: PropTypes.array, + filteredProjects: PropTypes.array, + checkedProjects: PropTypes.array +}; + +export default CsvModal; diff --git a/client/src/components/Projects/DeleteProjectModal.js b/client/src/components/Projects/DeleteProjectModal.js index da388db7..ac374617 100644 --- a/client/src/components/Projects/DeleteProjectModal.js +++ b/client/src/components/Projects/DeleteProjectModal.js @@ -1,7 +1,6 @@ import React from "react"; import { PropTypes } from "prop-types"; -import { faTrashCan } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { MdDelete, MdRestoreFromTrash } from "react-icons/md"; import Button from "../Button/Button"; import WarningIcon from "../../images/warning-icon.png"; import { createUseStyles, useTheme } from "react-jss"; @@ -43,9 +42,9 @@ const DeleteProjectModal = ({ mounted, onClose, project }) => { <>
    - Restore Project from Trash + Restore Project from Trash
    { ) : ( <>
    - Delete Project + Delete Project
    { className={classes.warningIcon} alt="Warning" /> - Are you sure you want to delete the project, + Are you sure you want to delete the following?

    (It will + remain in the recycling bin for ninety days

    before being + permanently deleted)
    )} -
    - {project.name}? + {Array.isArray(project.name) ? project.name.join(", ") : project.name}

    Filters

    @@ -182,7 +187,9 @@ FilterPopup.propTypes = { criteria: PropTypes.any, setCriteria: PropTypes.func, collapsed: PropTypes.bool, - setCollapsed: PropTypes.func + setCollapsed: PropTypes.func, + setCheckedProjectIds: PropTypes.func, + setSelectAllChecked: PropTypes.func }; export default FilterPopup; diff --git a/client/src/components/Projects/MultiProjectToolbarMenu.js b/client/src/components/Projects/MultiProjectToolbarMenu.js new file mode 100644 index 00000000..3c6fc845 --- /dev/null +++ b/client/src/components/Projects/MultiProjectToolbarMenu.js @@ -0,0 +1,230 @@ +import React, { useContext, useRef } from "react"; +import UserContext from "../../contexts/UserContext"; +import PropTypes from "prop-types"; +import { createUseStyles } from "react-jss"; +import { FaFileCsv } from "react-icons/fa"; +import { + MdDelete, + MdRestoreFromTrash, + MdPrint, + MdVisibility, + MdVisibilityOff +} from "react-icons/md"; +import { Tooltip } from "react-tooltip"; +import PdfPrint from "../PdfPrint/PdfPrint"; +import { useReactToPrint } from "react-to-print"; + +const useStyles = createUseStyles({ + container: { + display: "flex", + flexDirection: "row", + alignItems: "center", + marginLeft: "0.5em", + marginBottom: "-1em" + }, + list: { + display: "flex", + flexDirection: "row", + listStyleType: "none", + justifyContent: "space-between", + alignItems: "center", + width: "6.5em" + }, + button: { + border: "none", + padding: 0, + background: "none" + }, + multiStatus: { + color: "#002E6D" + } +}); + +const MultiProjectToolbarMenu = ({ + handleHideBoxes, + handleCsvModalOpen, + handleDeleteModalOpen, + checkedProjectIds, + criteria, + checkedProjectsStatusData, + pdfProjectData +}) => { + const printRef = useRef(null); + const classes = useStyles(); + const userContext = useContext(UserContext); + const account = userContext.account; + let project = null; + if ( + checkedProjectIds.length === 1 && + Object.keys(checkedProjectsStatusData).length > 0 + ) { + project = checkedProjectsStatusData; + } + const isProjectOwner = account.id === project?.loginId; + + const isBtnDisabled = (projProp, criteriaProp) => { + const sameDateVals = checkedProjectsStatusData[projProp] !== false; + const criteriaFilter = criteria[criteriaProp] === "all"; + + // disable button if current user is not the owner + // or if criteria is "all" and the date values are different + return !isProjectOwner || (criteriaFilter && !sameDateVals); + }; + + const isHideBtnDisabled = isBtnDisabled("dateHidden", "visibility"); + const isDelBtnDisabled = isBtnDisabled("dateTrashed", "status"); + + const tooltipMsg = (criteriaProp, msg, dateProp) => { + if (checkedProjectIds.length === 0) return; + + if (!isProjectOwner) { + return "You have selected a project that does not belong to you"; + } + + // show recover message if project is deleted + if (checkedProjectsStatusData.dateTrashed && criteriaProp === "status") { + return "Restore from Trash"; + } + + // show message when selecting mixed types (e.g. hide & unhide) + if ( + checkedProjectIds.length > 1 && + checkedProjectsStatusData[dateProp] === false + ) { + return criteria[criteriaProp] === "all" ? msg : ""; + } + }; + + const hasPdfData = () => { + return pdfProjectData && !!pdfProjectData.pdf; + }; + + const handlePrintPdf = useReactToPrint({ + content: () => printRef.current, + bodyClass: "printContainer", + pageStyle: ".printContainer {overflow: hidden;}" + }); + + return ( +
    +
    + {checkedProjectIds.length} Projects Selected +
    +
      +
    • + +
    • +
    • + + {checkedProjectIds.length !== 1 ? ( + + ) : ( + "" + )} + {project && hasPdfData() && ( +
      + +
      + )} +
    • +
    • + +
    • +
    • + +
    • +
    +
    + ); +}; + +MultiProjectToolbarMenu.propTypes = { + handleHideBoxes: PropTypes.func.isRequired, + handleCsvModalOpen: PropTypes.func.isRequired, + handleDeleteModalOpen: PropTypes.func.isRequired, + checkedProjectIds: PropTypes.arrayOf(PropTypes.number).isRequired, + criteria: PropTypes.object.isRequired, + checkedProjectsStatusData: PropTypes.object.isRequired, + pdfProjectData: PropTypes.object +}; + +export default MultiProjectToolbarMenu; diff --git a/client/src/components/Projects/ProjectContextMenu.js b/client/src/components/Projects/ProjectContextMenu.js index c2e7bce9..93cbb2ab 100644 --- a/client/src/components/Projects/ProjectContextMenu.js +++ b/client/src/components/Projects/ProjectContextMenu.js @@ -3,17 +3,17 @@ import UserContext from "../../contexts/UserContext"; import PropTypes from "prop-types"; import { createUseStyles } from "react-jss"; +import { FaFileCsv } from "react-icons/fa"; import { - faPrint, - faEye, - faEyeSlash, - faCamera, - faTrash, - faClone, - faFileCsv, - faPencil -} from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + MdPrint, + MdVisibility, + MdVisibilityOff, + MdCamera, + MdDelete, + MdRestoreFromTrash, + MdFileCopy, + MdEdit +} from "react-icons/md"; const useStyles = createUseStyles({ list: { @@ -44,11 +44,11 @@ const useStyles = createUseStyles({ const ProjectContextMenu = ({ project, closeMenu, + handleCsvModalOpen, handleCopyModalOpen, handleDeleteModalOpen, handleSnapshotModalOpen, handleRenameSnapshotModalOpen, - handleDownloadCsv, handlePrintPdf, handleHide }) => { @@ -68,8 +68,7 @@ const ProjectContextMenu = ({ className={classes.listItem} onClick={() => handleClick(handleRenameSnapshotModalOpen)} > - @@ -79,8 +78,7 @@ const ProjectContextMenu = ({ {project.dateSnapshotted && project.loginId !== account.id ? (
  • - @@ -93,8 +91,7 @@ const ProjectContextMenu = ({ className={classes.listItem} onClick={() => handleClick(handleSnapshotModalOpen)} > - @@ -106,19 +103,17 @@ const ProjectContextMenu = ({ onClick={() => handleClick(handlePrintPdf)} className={classes.listItem} > - Print Summary
  • handleClick(handleDownloadCsv)} + onClick={() => handleClick(() => handleCsvModalOpen(project))} className={classes.listItem} > - @@ -128,8 +123,7 @@ const ProjectContextMenu = ({ onClick={() => handleClick(handleCopyModalOpen)} className={classes.listItem} > - @@ -142,8 +136,7 @@ const ProjectContextMenu = ({ > {project.dateHidden ? ( <> - @@ -151,8 +144,7 @@ const ProjectContextMenu = ({ ) : ( <> - @@ -165,26 +157,24 @@ const ProjectContextMenu = ({
  • handleClick(handleDeleteModalOpen)} className={classes.listItem} - style={{ borderTop: "1px solid black", color: "red" }} + style={{ borderTop: "1px solid black" }} > {project.dateTrashed ? ( - <> - + Restore from Trash - + ) : ( - <> - + Delete - + )}
  • )} @@ -195,11 +185,11 @@ const ProjectContextMenu = ({ ProjectContextMenu.propTypes = { project: PropTypes.object, closeMenu: PropTypes.func, + handleCsvModalOpen: PropTypes.func, handleCopyModalOpen: PropTypes.func, handleDeleteModalOpen: PropTypes.func, handleSnapshotModalOpen: PropTypes.func, handleRenameSnapshotModalOpen: PropTypes.func, - handleDownloadCsv: PropTypes.func, handlePrintPdf: PropTypes.func, handleHide: PropTypes.func }; diff --git a/client/src/components/Projects/ProjectTableRow.js b/client/src/components/Projects/ProjectTableRow.js index 18921233..1db1d12d 100644 --- a/client/src/components/Projects/ProjectTableRow.js +++ b/client/src/components/Projects/ProjectTableRow.js @@ -1,20 +1,13 @@ import React from "react"; import { Link } from "react-router-dom"; import { createUseStyles } from "react-jss"; -import { useEffect, useRef, useState } from "react"; +import { useState, useRef, useEffect } from "react"; import PropTypes from "prop-types"; import Popup from "reactjs-popup"; import "reactjs-popup/dist/index.css"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - faEye, - faEyeSlash, - faEllipsisV -} from "@fortawesome/free-solid-svg-icons"; -import moment from "moment"; -import { CSVLink } from "react-csv"; +import { MdVisibility, MdVisibilityOff, MdMoreVert } from "react-icons/md"; +import { formatDate } from "../../helpers/util"; import { useReactToPrint } from "react-to-print"; - import ProjectContextMenu from "./ProjectContextMenu"; import PdfPrint from "../PdfPrint/PdfPrint"; import fetchEngineRules from "./fetchEngineRules"; @@ -46,220 +39,28 @@ const useStyles = createUseStyles({ } }); -const mapCsvRules = (project, rules) => { - // Specify which rules we want to include in the CSV, and in what order by code. - const includeRuleCodes = [ - "PROJECT_NAME", - "PROJECT_ADDRESS", - "APN", - "VERSION_NO", - "BUILDING_PERMIT", - "CASE_NO_LADOT", - "CASE_NO_PLANNING", - "PROJECT_DESCRIPTION", - "PROJECT_LEVEL", - "PARK_REQUIREMENT", - "PARK_SPACES", - "CALC_PARK_RATIO", - "TARGET_POINTS_PARK", - "PTS_EARNED", - "PTS_PKG_RESIDENTIAL_COMMERCIAL", - "PTS_PKG_SCHOOL", - "STRATEGY_AFFORDABLE", - "STRATEGY_BIKE_1", - "STRATEGY_BIKE_3", - "STRATEGY_BIKE_4", - "STRATEGY_BIKE_5", - "STRATEGY_CAR_SHARE_1", - "STRATEGY_CAR_SHARE_3", - "STRATEGY_CAR_SHARE_4", - "STRATEGY_CAR_SHARE_ELECTRIC", - "STRATEGY_CAR_SHARE_BONUS", - "STRATEGY_CHILD_CARE", - "STRATEGY_HOV_2", - "STRATEGY_HOV_3", - "STRATEGY_HOV_4", - "STRATEGY_HOV_5", - "STRATEGY_INFO_1", - "STRATEGY_INFO_2", - "STRATEGY_INFO_3", - "STRATEGY_INFO_5", - "STRATEGY_MIXED_USE", - "STRATEGY_MOBILITY_INVESTMENT_1", - "STRATEGY_MOBILITY_INVESTMENT_2", - "STRATEGY_PARKING_1", - "STRATEGY_PARKING_2", - "STRATEGY_PARKING_3", - "STRATEGY_PARKING_4", - "STRATEGY_PARKING_5", - "STRATEGY_SHARED_MOBILITY_1", - "STRATEGY_SHARED_MOBILITY_2", - "STRATEGY_TELECOMMUTE_1", - "STRATEGY_TELECOMMUTE_2", - "STRATEGY_TRANSIT_ACCESS_1", - "STRATEGY_TRANSIT_ACCESS_3", - "STRATEGY_TRANSIT_ACCESS_4", - "STRATEGY_TRANSIT_ACCESS_5", - "STRATEGY_TMO_1", - "STRATEGY_TMO_2", - "STRATEGY_APPLICANT", - "UNITS_CONDO", - "PARK_CONDO", - "UNITS_HABIT_LT3", - "UNITS_HABIT_3", - "UNITS_HABIT_GT3", - "AFFORDABLE_HOUSING", - "UNITS_GUEST", - "SF_RETAIL", - "SF_FURNITURE", - "SF_RESTAURANT", - "SF_HEALTH_CLUB", - "SF_RESTAURANT_TAKEOUT", - "SF_OFFICE", - "SF_INST_GOV", - "SF_INST_OTHER", - "SF_INDUSTRIAL", - "SF_WAREHOUSE", - "SF_INST_MEDICAL_SVC", - "SF_HOSPITAL", - "STUDENTS_ELEMENTARY", - "CLASSROOM_SCHOOL", - "STUDENTS_TRADE_SCHOOL", - "HS_STUDENTS", - "HS_AUDITORIUM_SEATS", - "HS_AUDITORIUM_SF", - "SEAT_AUDITORIUM", - "SF_AUDITORIUM_NO_SEATS" - ]; - - // Get the needed data from the rule calculation - // flatMap/filter improves testability by allowing missing rules. - const orderedRules = includeRuleCodes - .flatMap(rc => rules.find(r => r.code === rc)) - .filter(r => !!r) - .map(r => ({ - code: r.code, - name: r.name, - dataType: r.dataType, - choices: r.choices, - value: r.value, - units: r.units - })); - - // Augment with meta-data from project table that is not included in rules. - const projectProperties = [ - { - code: "Id", - name: "Project Id", - dataType: "project", - choices: null, - value: project.id - }, - { - code: "Author FN", - name: "Author First Name", - dataType: "project", - choices: null, - value: project.firstName - }, - { - code: "Author LN", - name: "Author Last Name", - dataType: "project", - choices: null, - value: project.lastName - }, - { - code: "Date Created", - name: "Date Created", - dataType: "project", - choices: null, - value: project.dateCreated - }, - { - code: "Date Modified", - name: "Date Modified", - dataType: "project", - choices: null, - value: project.dateModified - }, - { - code: "Date Hidden", - name: "Date Hidden", - dataType: "project", - choices: null, - value: project.dateHidden - }, - { - code: "Date Deleted", - name: "Date Deleted", - dataType: "project", - choices: null, - value: project.dateTrashed - }, - { - code: "Date Snapshotted", - name: "Date Snapshotted", - dataType: "project", - choices: null, - value: project.dateSnapshotted - } - ]; - - let columnData = projectProperties.concat(orderedRules); - - const ruleNames = columnData.flatMap(rule => { - if (rule.dataType === "choice") { - return rule.choices.map(choice => rule.name + " - " + choice.name); - } else { - return `${rule.name}${rule.units ? " (" + rule.units + ")" : ""}`; - } - }); - - const ruleValues = columnData.flatMap(rule => { - if (rule.dataType === "choice") { - return rule.choices.map(choice => - choice.id == rule.value || (choice.id == 0 && !rule.value) ? "Y" : "N" - ); - } else { - return rule.value ? rule.value.toString() : ""; - } - }); - - const flat = [ - ["TDM Calculation Project Summary"], - ["Date Printed: " + Date().toString()], // TODO: prefer ISO string? - [], - ruleNames, - ruleValues - ]; - return flat; -}; - const ProjectTableRow = ({ project, + handleCsvModalOpen, handleCopyModalOpen, handleDeleteModalOpen, handleSnapshotModalOpen, handleRenameSnapshotModalOpen, - handleHide + handleHide, + handleCheckboxChange, + checkedProjectIds }) => { const classes = useStyles(); - const momentModified = moment(project.dateModified); const formInputs = JSON.parse(project.formInputs); - - const csvRef = useRef(); // setup the ref that we'll use for the hidden CsvLink click once we've updated the data const printRef = useRef(); - const [projectData, setProjectData] = useState(); + const [projectRules, setProjectRules] = useState(null); - // Download and process rules once for both CSV and PDF rendering + // Download and process rules for PDF rendering useEffect(() => { const fetchRules = async () => { - const rules = await fetchEngineRules(project); - const csvData = project && mapCsvRules(project, rules || []); - - setProjectData({ pdf: rules, csv: csvData }); + const result = await fetchEngineRules(project); + setProjectRules(result); }; fetchRules() @@ -267,10 +68,6 @@ const ProjectTableRow = ({ .catch(console.error); }, [project]); - const handleDownloadCsv = () => { - csvRef.current.link.click(); - }; - const handlePrintPdf = useReactToPrint({ content: () => printRef.current, bodyClass: "printContainer", @@ -287,27 +84,34 @@ const ProjectTableRow = ({ if (project.dateTrashed) { return ( - {moment(project.dateTrashed).format("YYYY-MM-DD")} + {formatDate(project.dateTrashed)} -Deleted ); } - return {moment(project.dateModified).format("YYYY-MM-DD")}; + + return {formatDate(project.dateModified)}; }; return ( + + handleCheckboxChange(project.id)} + /> + {project.dateHidden ? ( - ) : ( - {project.address} {fallbackToBlank(formInputs.VERSION_NO)} - {`${project.firstName} ${project.lastName}`} + + {`${project.firstName} ${project.lastName}`} + - {moment(project.dateCreated).format("YYYY-MM-DD")} + {formatDate(project.dateCreated)} {dateModifiedDisplay()} - {projectData && ( + {projectRules && (
    - + } position="left center" @@ -352,9 +153,9 @@ const ProjectTableRow = ({ handleCsvModalOpen(ev, project)} handleCopyModalOpen={handleCopyModalOpen} handleDeleteModalOpen={handleDeleteModalOpen} - handleDownloadCsv={handleDownloadCsv} handlePrintPdf={handlePrintPdf} handleSnapshotModalOpen={handleSnapshotModalOpen} handleRenameSnapshotModalOpen={handleRenameSnapshotModalOpen} @@ -363,17 +164,7 @@ const ProjectTableRow = ({ )}
    - - +
    )} @@ -384,11 +175,14 @@ const ProjectTableRow = ({ ProjectTableRow.propTypes = { project: PropTypes.object.isRequired, + handleCsvModalOpen: PropTypes.func.isRequired, handleCopyModalOpen: PropTypes.func.isRequired, handleDeleteModalOpen: PropTypes.func.isRequired, handleSnapshotModalOpen: PropTypes.func.isRequired, handleRenameSnapshotModalOpen: PropTypes.func.isRequired, - handleHide: PropTypes.func.isRequired + handleHide: PropTypes.func.isRequired, + handleCheckboxChange: PropTypes.func.isRequired, + checkedProjectIds: PropTypes.arrayOf(PropTypes.number).isRequired }; export default ProjectTableRow; diff --git a/client/src/components/Projects/ProjectsPage.js b/client/src/components/Projects/ProjectsPage.js index 8ad168a9..b369f297 100644 --- a/client/src/components/Projects/ProjectsPage.js +++ b/client/src/components/Projects/ProjectsPage.js @@ -1,27 +1,26 @@ -import React, { useState, useContext } from "react"; +import React, { useState, useContext, useEffect, memo } from "react"; import PropTypes from "prop-types"; import { useNavigate } from "react-router-dom"; import { createUseStyles } from "react-jss"; import UserContext from "../../contexts/UserContext.js"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - faSortUp, - faSortDown, - faFilter -} from "@fortawesome/free-solid-svg-icons"; +import { MdFilterAlt, MdArrowDropDown, MdArrowDropUp } from "react-icons/md"; import SearchIcon from "../../images/search.png"; -import Pagination from "../ProjectWizard/Pagination.js"; +import Pagination from "../UI/Pagination.js"; import ContentContainerNoSidebar from "../Layout/ContentContainerNoSidebar"; import useErrorHandler from "../../hooks/useErrorHandler"; import useProjects from "../../hooks/useGetProjects"; +import useCheckedProjectsStatusData from "../../hooks/useCheckedProjectsStatusData.js"; import * as projectService from "../../services/project.service"; import SnapshotProjectModal from "./SnapshotProjectModal"; import RenameSnapshotModal from "./RenameSnapshotModal"; import DeleteProjectModal from "./DeleteProjectModal"; import CopyProjectModal from "./CopyProjectModal"; +import CsvModal from "./CsvModal.js"; import ProjectTableRow from "./ProjectTableRow"; import FilterDrawer from "./FilterDrawer.js"; +import MultiProjectToolbarMenu from "./MultiProjectToolbarMenu.js"; +import fetchEngineRules from "./fetchEngineRules.js"; const useStyles = createUseStyles({ outerDiv: { @@ -49,7 +48,7 @@ const useStyles = createUseStyles({ flexBasis: "1%", flexShrink: 0, flexGrow: 0, - transition: "flex-basis 1s ease-in-out" + transition: "flex-basis 0.5s ease-in-out" }, pageTitle: { marginTop: "2em" @@ -118,6 +117,26 @@ const useStyles = createUseStyles({ overflow: "auto", width: "100%", margin: "20px 0px" + }, + pageContainer: { + display: "flex", + flexDirection: "row", + alignItems: "center", + justifyContent: "center" + }, + dropContent: { + padding: "5px", + borderColor: "silver", + borderRadius: "4px" + }, + optionItems: { + backgroundColor: "white", + "&:hover": { + backgroundColor: "silver" + } + }, + itemsPerPage: { + marginLeft: "5px" } }); @@ -127,7 +146,7 @@ const ProjectsPage = ({ contentContainerRef }) => { const [filterText, setFilterText] = useState(""); const [order, setOrder] = useState("asc"); - const email = userContext.account.email; + const email = userContext.account ? userContext.account.email : ""; const navigate = useNavigate(); const handleError = useErrorHandler(email, navigate); const [projects, setProjects] = useProjects(handleError); @@ -136,11 +155,27 @@ const ProjectsPage = ({ contentContainerRef }) => { const [snapshotModalOpen, setSnapshotModalOpen] = useState(false); const [renameSnapshotModalOpen, setRenameSnapshotModalOpen] = useState(false); const [deleteModalOpen, setDeleteModalOpen] = useState(false); + const [csvModalOpen, setCsvModalOpen] = useState(false); const [selectedProject, setSelectedProject] = useState(null); + const [checkedProjectIds, setCheckedProjectIds] = useState([]); + const [selectAllChecked, setSelectAllChecked] = useState(false); + const [projectData, setProjectData] = useState(); const [currentPage, setCurrentPage] = useState(1); + const [perPage, setPerPage] = useState(10); + const projectsPerPage = perPage; - const projectsPerPage = 10; - const highestPage = Math.ceil(projects.length / projectsPerPage); + const handlePerPageChange = newPerPage => { + setPerPage(newPerPage); + const newHighestPage = Math.ceil(sortedProjects.length / newPerPage); + + if (currentPage > newHighestPage) { + setCurrentPage(1); + } + }; + + const getCheckedProjects = checkedProjectIds.map(id => + projects.find(p => p.id === id) + ); const [criteria, setCriteria] = useState({ type: "all", @@ -156,9 +191,36 @@ const ProjectsPage = ({ contentContainerRef }) => { endDateModified: null }); const [filterCollapsed, setFilterCollapsed] = useState(true); + const checkedProjectsStatusData = useCheckedProjectsStatusData( + checkedProjectIds, + projects + ); + + // fetching rules for PDF + useEffect(() => { + const fetchRules = async () => { + let project; + + if ( + checkedProjectIds.length === 1 && + Object.keys(checkedProjectsStatusData).length > 0 + ) { + project = checkedProjectsStatusData; + } + + if (project && project.id && project.calculationId) { + const rules = await fetchEngineRules(project); + setProjectData({ pdf: rules }); + } + }; + + fetchRules().catch(console.error); + }, [checkedProjectIds, checkedProjectsStatusData]); + + const MemoizedMultiProjectToolbar = memo(MultiProjectToolbarMenu); const selectedProjectName = (() => { - if (!selectedProject) { + if (!selectedProject || !selectedProject.formInputs) { return ""; } const projectFormInputsAsJson = JSON.parse(selectedProject.formInputs); @@ -166,13 +228,17 @@ const ProjectsPage = ({ contentContainerRef }) => { })(); const paginate = pageNumber => { + const newHighestPage = Math.ceil(sortedProjects.length / perPage); if (typeof pageNumber === "number") { setCurrentPage(pageNumber); } else if (pageNumber === "left" && currentPage !== 1) { setCurrentPage(currentPage - 1); - } else if (pageNumber === "right" && currentPage !== highestPage) { + } else if (pageNumber === "right" && currentPage < newHighestPage) { setCurrentPage(currentPage + 1); } + // uncheck Projects on page change + setCheckedProjectIds([]); + setSelectAllChecked(false); }; const handleCopyModalOpen = project => { @@ -191,13 +257,16 @@ const ProjectsPage = ({ contentContainerRef }) => { if (action === "ok") { const projectFormInputsAsJson = JSON.parse(selectedProject.formInputs); projectFormInputsAsJson.PROJECT_NAME = newProjectName; - + let newProject = { + ...selectedProject, + name: newProjectName, + formInputs: JSON.stringify(projectFormInputsAsJson) + }; + if (!newProject.description) { + newProject.description = ""; + } try { - await projectService.post({ - ...selectedProject, - name: newProjectName, - formInputs: JSON.stringify(projectFormInputsAsJson) - }); + await projectService.post(newProject); await updateProjects(); } catch (err) { handleError(err); @@ -207,23 +276,41 @@ const ProjectsPage = ({ contentContainerRef }) => { }; const handleDeleteModalOpen = project => { - setSelectedProject(project); + if (!checkedProjectIds.length) setSelectedProject(project); setDeleteModalOpen(true); }; const handleDeleteModalClose = async action => { if (action === "ok") { + const projectIDs = selectedProject + ? [selectedProject.id] + : checkedProjectIds; + const dateTrashed = selectedProject + ? !selectedProject.dateTrashed + : !checkedProjectsStatusData.dateTrashed; + try { - await projectService.trash( - [selectedProject.id], - !selectedProject.dateTrashed - ); + await projectService.trash(projectIDs, dateTrashed); await updateProjects(); } catch (err) { handleError(err); } } setDeleteModalOpen(false); + setSelectedProject(null); + setCheckedProjectIds([]); + setSelectAllChecked(false); + }; + + const handleCsvModalOpen = (event, project) => { + // If invoked from kebab menu, project will be the selected project. + // If invoked from MultiProjectToolbarMenu, want to reset selected project to null + setSelectedProject(project || null); + setCsvModalOpen(true); + }; + + const handleCsvModalClose = async () => { + setCsvModalOpen(false); }; const handleSnapshotModalOpen = project => { @@ -269,10 +356,65 @@ const ProjectsPage = ({ contentContainerRef }) => { }; const handleHide = async project => { - setSelectedProject(project); - await projectService.hide([project.id], !project.dateHidden); - await updateProjects(); - console.error(project.dateHidden); + try { + if (!checkedProjectIds.length) { + setSelectedProject(project); + } + + const projectIDs = + checkedProjectIds.length > 0 ? checkedProjectIds : [project.id]; + const dateHidden = + checkedProjectIds.length > 0 + ? !checkedProjectsStatusData.dateHidden + : !project.dateHidden; + + await projectService.hide(projectIDs, dateHidden); + await updateProjects(); + } catch (err) { + console.error(err); + } + + setSelectedProject(null); + setCheckedProjectIds([]); + setSelectAllChecked(false); + }; + + const handleCheckboxChange = projectId => { + setCheckedProjectIds(prevCheckedProjectIds => { + if (prevCheckedProjectIds.includes(projectId)) { + return prevCheckedProjectIds.filter(id => id !== projectId); + } else { + return [...prevCheckedProjectIds, projectId]; + } + }); + + // header checkbox status + setSelectAllChecked(checkedProjectIds.length === currentProjects.length); + }; + + const handleHeaderCheckbox = () => { + if (!selectAllChecked) { + setCheckedProjectIds( + currentProjects + .filter( + p => + (criteria.visibility === "visible" && !p.dateHidden) || + (criteria.visibility === "hidden" && p.dateHidden) || + criteria.visibility === "all" + ) + .filter( + p => + (criteria.status === "active" && !p.dateTrashed) || + (criteria.status === "deleted" && p.dateTrashed) || + criteria.status === "all" + ) + .map(p => p.id) + ); + } else { + setCheckedProjectIds([]); + } + + setSelectAllChecked(!selectAllChecked); }; const descCompareBy = (a, b, orderBy) => { @@ -330,6 +472,9 @@ const ProjectsPage = ({ contentContainerRef }) => { }; const handleSort = property => { + // disable sorting for header checkbox + if (property === "checkAllProjects") return; + const isAsc = orderBy === property && order === "asc"; setOrder(isAsc ? "desc" : "asc"); setOrderBy(property); @@ -390,9 +535,14 @@ const ProjectsPage = ({ contentContainerRef }) => { !p.fullname.toLowerCase().includes(criteria.author.toLowerCase()) ) return false; - p.alternative = JSON.parse(p["formInputs"]).VERSION_NO - ? JSON.parse(p["formInputs"]).VERSION_NO - : ""; + try { + p.alternative = JSON.parse(p["formInputs"]).VERSION_NO + ? JSON.parse(p["formInputs"]).VERSION_NO + : ""; + } catch (err) { + p.alternative = JSON.stringify(err, null, 2); + } + if ( criteria.alternative && !p.alternative.toLowerCase().includes(criteria.alternative.toLowerCase()) @@ -416,6 +566,19 @@ const ProjectsPage = ({ contentContainerRef }) => { }; const headerData = [ + { + id: "checkAllProjects", + label: ( + + ) + }, { id: "dateHidden", label: "Visibility" @@ -458,6 +621,8 @@ const ProjectsPage = ({ contentContainerRef }) => { setCriteria={setCriteria} collapsed={filterCollapsed} setCollapsed={setFilterCollapsed} + setCheckedProjectIds={setCheckedProjectIds} + setSelectAllChecked={setSelectAllChecked} />
    @@ -480,39 +645,52 @@ const ProjectsPage = ({ contentContainerRef }) => { style={{ display: "flex", flexDirection: "row", - justifyContent: "space-around", - alignSelf: "flex-end" + justifyContent: "space-between" }} > -
    - handleFilterTextChange(e.target.value)} - /> - Search Icon -
    - {filterCollapsed ? ( - - ) : null} + Search Icon +
    + {filterCollapsed ? ( + + ) : null} +
    @@ -538,13 +716,11 @@ const ProjectsPage = ({ contentContainerRef }) => { {label}{" "} {order === "asc" ? ( - ) : ( - )} @@ -563,6 +739,7 @@ const ProjectsPage = ({ contentContainerRef }) => { { handleRenameSnapshotModalOpen } handleHide={handleHide} + handleCheckboxChange={handleCheckboxChange} + checkedProjectIds={checkedProjectIds} /> )) ) : ( @@ -582,14 +761,54 @@ const ProjectsPage = ({ contentContainerRef }) => {
    - +
    + + +
    - {selectedProject && ( + {(selectedProject || checkedProjectsStatusData) && ( <> + { { /> )} - {/*
    - {rules ? ( - - ) : ( -
    duh
    - )} -
    */}
    diff --git a/client/src/components/Projects/SnapshotProjectModal.js b/client/src/components/Projects/SnapshotProjectModal.js index e12af207..72630c41 100644 --- a/client/src/components/Projects/SnapshotProjectModal.js +++ b/client/src/components/Projects/SnapshotProjectModal.js @@ -3,9 +3,7 @@ import PropTypes from "prop-types"; import { createUseStyles, useTheme } from "react-jss"; import Button from "../Button/Button"; -import { faCopy } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - +import { MdFileCopy } from "react-icons/md"; import ModalDialog from "../UI/AriaModal/ModalDialog"; const useStyles = createUseStyles(theme => ({ @@ -39,7 +37,7 @@ export default function SnapshotProjectModal({ initialFocus="#duplicateName" >
    - Convert " + Convert " {`${selectedProjectName}`}" Into a Snapshot?
    diff --git a/client/src/components/Projects/csvData.js b/client/src/components/Projects/csvData.js new file mode 100644 index 00000000..8a3b8f00 --- /dev/null +++ b/client/src/components/Projects/csvData.js @@ -0,0 +1,261 @@ +import fetchEngineRules from "./fetchEngineRules"; + +// This method gets properties from the project object and converts them to +// an array of objects that look enough like rules to concatenate with an +// array of actual rules for purposes of processing the combined data, including +// a dummy code value that can be used to sort the colums in the order specified by +// the orderedCodes array below. +const getProjectProperties = project => { + return [ + { + code: "Date Created", + name: "Date Created", + dataType: "project", + choices: null, + value: project.dateCreated + }, + { + code: "Date Modified", + name: "Date Modified", + dataType: "project", + choices: null, + value: project.dateModified + }, + { + code: "Date Snapshotted", + name: "Date Snapshotted", + dataType: "project", + choices: null, + value: project.dateSnapshotted + }, + { + code: "Date Hidden", + name: "Date Hidden", + dataType: "project", + choices: null, + value: project.dateHidden + }, + { + code: "Date Deleted", + name: "Date Deleted", + dataType: "project", + choices: null, + value: project.dateTrashed + }, + { + code: "Author FN", + name: "Author First Name", + dataType: "project", + choices: null, + value: project.firstName + }, + { + code: "Author LN", + name: "Author Last Name", + dataType: "project", + choices: null, + value: project.lastName + }, + { + code: "Id", + name: "Project Id", + dataType: "project", + choices: null, + value: project.id + } + ]; +}; + +// Used to determine which rules (and project properties) to include as collumns in the CSV, +// in the order they should appear in the CSV file. +const orderedCodes = [ + "PROJECT_NAME", + "PROJECT_ADDRESS", + "APN", + "BUILDING_PERMIT", + "CASE_NO_LADOT", + "CASE_NO_PLANNING", + "PROJECT_DESCRIPTION", + "VERSION_NO", + "Date Created", + "Date Modified", + "Date Snapshotted", + "Date Hidden", + "Date Deleted", + "Author FN", + "Author LN", + "Id", + "PROJECT_LEVEL", + "TARGET_POINTS_PARK", + "PTS_EARNED", + "UNITS_CONDO", + "PARK_CONDO", + "UNITS_HABIT_LT3", + "UNITS_HABIT_3", + "UNITS_HABIT_GT3", + "AFFORDABLE_HOUSING", + "UNITS_GUEST", + "SF_RETAIL", + "SF_FURNITURE", + "SF_RESTAURANT", + "SF_HEALTH_CLUB", + "SF_RESTAURANT_TAKEOUT", + "SF_OFFICE", + "SF_INST_GOV", + "SF_INST_OTHER", + "SF_INDUSTRIAL", + "SF_WAREHOUSE", + "SF_INST_MEDICAL_SVC", + "SF_HOSPITAL", + "STUDENTS_ELEMENTARY", + "CLASSROOM_SCHOOL", + "STUDENTS_TRADE_SCHOOL", + "HS_STUDENTS", + "HS_AUDITORIUM_SEATS", + "HS_AUDITORIUM_SF", + "SEAT_AUDITORIUM", + "SF_AUDITORIUM_NO_SEATS", + "PARK_REQUIREMENT", + "PARK_SPACES", + "CALC_PARK_RATIO", + "PTS_PKG_RESIDENTIAL_COMMERCIAL", + "PTS_PKG_SCHOOL", + "STRATEGY_AFFORDABLE", + "STRATEGY_BIKE_1", + "STRATEGY_BIKE_3", + "STRATEGY_BIKE_4", + "STRATEGY_BIKE_5", + "PTS_BIKE_BONUS", + "STRATEGY_CAR_SHARE_1", + "STRATEGY_CAR_SHARE_3", + "STRATEGY_CAR_SHARE_4", + "STRATEGY_CAR_SHARE_ELECTRIC", + "PTS_CAR_SHARE_BONUS", + "STRATEGY_CHILD_CARE", + "STRATEGY_HOV_2", + "STRATEGY_HOV_3", + "STRATEGY_HOV_4", + "STRATEGY_HOV_5", + "STRATEGY_INFO_1", + "STRATEGY_INFO_2", + "STRATEGY_INFO_3", + "STRATEGY_INFO_5", + "STRATEGY_MIXED_USE", + "STRATEGY_MOBILITY_INVESTMENT_1", + "STRATEGY_MOBILITY_INVESTMENT_2", + "STRATEGY_PARKING_1", + "STRATEGY_PARKING_2", + "STRATEGY_PARKING_3", + "STRATEGY_PARKING_4", + "STRATEGY_PARKING_5", + "STRATEGY_SHARED_MOBILITY_1", + "STRATEGY_SHARED_MOBILITY_2", + "STRATEGY_TELECOMMUTE_1", + "STRATEGY_TELECOMMUTE_2", + "STRATEGY_TRANSIT_ACCESS_1", + "STRATEGY_TRANSIT_ACCESS_3", + "STRATEGY_TRANSIT_ACCESS_4", + "STRATEGY_TRANSIT_ACCESS_5", + "STRATEGY_TMO_1", + "STRATEGY_TMO_2", + "STRATEGY_APPLICANT", + "STRATEGY_APPLICANT_COMMENT" +]; + +const getOrderedColumnData = (project, rules) => { + // Augment with meta-data from project table that is not included in rules. + const projectProperties = getProjectProperties(project); + + // Append data from project table to data from rules computation + const combinedRules = rules.concat(projectProperties); + + // Append a made-up "rule" to get user-defined strategy description + const udsRule = rules.find(f => f.code === "STRATEGY_APPLICANT"); + if (udsRule) { + combinedRules.push({ + code: "STRATEGY_APPLICANT_COMMENT", + name: "User-Defined Strategy Description", + dataType: "string", + choices: null, + value: udsRule.comment, + units: "" + }); + } + + // Get the needed data from the rule calculation + // flatMap/filter improves testability by allowing missing rules. + const columnData = orderedCodes + .flatMap(rc => combinedRules.find(r => r.code === rc)) + .filter(r => !!r) + .map(r => ({ + code: r.code, + name: r.name, + dataType: r.dataType, + choices: r.choices, + value: r.value, + units: r.units + })); + return columnData; +}; + +const getColumnNames = (project, rules) => { + const columnData = getOrderedColumnData(project, rules); + + const columnNames = columnData.flatMap(rule => { + if (rule.dataType === "choice") { + return rule.choices + .slice(1) + .map(choice => rule.name + " - " + choice.name); + } else { + return `${rule.name}${rule.units ? " (" + rule.units + ")" : ""}`; + } + }); + return columnNames; +}; + +const getColumnValues = (project, rules) => { + const columnData = getOrderedColumnData(project, rules); + + const cellValues = columnData.flatMap(rule => { + if (rule.dataType === "choice") { + return rule.choices + .slice(1) + .map(choice => + choice.id == rule.value || (choice.id == 0 && !rule.value) ? "Y" : "N" + ); + } else { + if (rule.dataType === "boolean") { + return rule.value ? "Y" : "N"; + } else if (rule.dataType === "number") { + return rule.value ? rule.value.toString() : "0"; + } + return rule.value ? rule.value.toString() : ""; + } + }); + return cellValues; +}; + +const getFileHeading = () => { + return [ + ["TDM Calculation Project Summary"], + ["Date Printed: " + Date().toString()], // TODO: prefer ISO string? + [] + ]; +}; + +const getCsvForProjects = async (projects, progressCallback) => { + let data = getFileHeading(); + if (projects.length != 0) { + for (let i = 0; i < projects.length; i++) { + const rules = await fetchEngineRules(projects[i]); + if (i == 0) { + data.push(getColumnNames(projects[i], rules)); + } + data.push(getColumnValues(projects[i], rules)); + if (progressCallback) progressCallback((i + 1) / projects.length); + } + } + return data; +}; + +export { getCsvForProjects }; diff --git a/client/src/components/Roles.js b/client/src/components/Roles.js index 89074959..5b958867 100644 --- a/client/src/components/Roles.js +++ b/client/src/components/Roles.js @@ -7,8 +7,7 @@ import { useToast } from "../contexts/Toast"; import UserContext from "../contexts/UserContext"; import Popup from "reactjs-popup"; import "reactjs-popup/dist/index.css"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faCheck, faEllipsisV } from "@fortawesome/free-solid-svg-icons"; +import { MdCheck, MdMoreVert } from "react-icons/md"; import RolesContextMenu from "./ArchiveDelete/RolesContextMenu"; import ContentContainer from "./Layout/ContentContainer"; @@ -189,154 +188,147 @@ const Roles = ({ contentContainerRef }) => { }; return ( -
    - + {redirectPath ? : null} +

    Security Roles

    +
    + Grant or Revoke Admin Permissions +
    +
    - {redirectPath ? : null} -

    Security Roles

    -
    - Grant or Revoke Admin Permissions -
    -
    + Find: + + { + setSearchString(e.target.value); + filt(accounts, e.target.value); }} - > - - { - setSearchString(e.target.value); - filt(accounts, e.target.value); - }} - data-testid="searchString" - /> -
    -
    - - View Archived Accounts - -
    + data-testid="searchString" + /> +
    +
    + + View Archived Accounts + +
    - - - - - - - - - - - - - - {filteredAccounts && - filteredAccounts.map(account => ( - - - - - - - - - - ))} - -
    EmailName - Admin - - Security Admin - - Email Confirmed - - Registration Date - - Options -
    {account.email} - {`${account.lastName}, ${account.firstName}`} - - onInputChange(e, account)} - name="isAdmin" - /> - - onInputChange(e, account)} - name="isSecurityAdmin" - /> - - {account.emailConfirmed ? ( - - ) : ( - "" - )} - - {new Date(account.dateCreated).toLocaleDateString("en-US", { - month: "numeric", - day: "numeric", - year: "numeric" - })} - - - - - } - position="bottom center" - offsetX={-100} - on="click" - closeOnDocumentClick - arrow={false} - onOpen={() => setHoveredRow(account.id)} - onClose={() => setHoveredRow(null)} - > -
    - -
    -
    -
    -
    -
    + + + + + + + + + + + + + + {filteredAccounts && + filteredAccounts.map(account => ( + + + + + + + + + + ))} + +
    EmailName + Admin + + Security Admin + + Email Confirmed + + Registration Date + + Options +
    {account.email} + {`${account.lastName}, ${account.firstName}`} + + onInputChange(e, account)} + name="isAdmin" + /> + + onInputChange(e, account)} + name="isSecurityAdmin" + /> + + {account.emailConfirmed ? ( + + ) : ( + "" + )} + + {new Date(account.dateCreated).toLocaleDateString("en-US", { + month: "numeric", + day: "numeric", + year: "numeric" + })} + + + + + } + position="bottom center" + offsetX={-100} + on="click" + closeOnDocumentClick + arrow={false} + onOpen={() => setHoveredRow(account.id)} + onClose={() => setHoveredRow(null)} + > +
    + +
    +
    +
    + ); }; diff --git a/client/src/components/ToolTip/ToolTipIcon.js b/client/src/components/ToolTip/ToolTipIcon.js index ea74063b..ba71ad7d 100644 --- a/client/src/components/ToolTip/ToolTipIcon.js +++ b/client/src/components/ToolTip/ToolTipIcon.js @@ -1,6 +1,5 @@ import React from "react"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faCircle, faQuestion } from "@fortawesome/free-solid-svg-icons"; +import { MdHelp } from "react-icons/md"; import { createUseStyles } from "react-jss"; import clsx from "clsx"; import { PropTypes } from "prop-types"; @@ -13,8 +12,8 @@ const useStyles = createUseStyles({ cursor: "pointer" } }, - circle: { - filter: "drop-shadow(0px 4px 2px rgba(0, 46, 109, 0.3))" + help: { + color: "#a7c539" }, tooltip: { color: "rgb(30, 36, 63) !important", @@ -45,16 +44,7 @@ const ToolTipIcon = ({ size = "small", id, tooltipContent }) => { data-iscapture="true" data-html="true" > - - + ); }; diff --git a/client/src/components/ToolTip/ToolTipLabel.js b/client/src/components/ToolTip/ToolTipLabel.js index f990d118..3ce55214 100644 --- a/client/src/components/ToolTip/ToolTipLabel.js +++ b/client/src/components/ToolTip/ToolTipLabel.js @@ -2,8 +2,7 @@ import React from "react"; import PropTypes from "prop-types"; import { createUseStyles } from "react-jss"; import clsx from "clsx"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faInfo, faCircle } from "@fortawesome/free-solid-svg-icons"; +import { MdInfo } from "react-icons/md"; const useStyles = createUseStyles(theme => ({ labelWrapper: { @@ -68,10 +67,7 @@ const useStyles = createUseStyles(theme => ({ color: theme.colors.warning } }, - faInfoIcon: { - color: "#ffffff" - }, - faCircle: { + infoIcon: { color: "#002E6D" }, iconContainer: { @@ -138,12 +134,8 @@ const ToolTipLabel = ({ className={clsx("fa-layers fa-fw", classes.iconContainer)} style={showDescription ? { visibility: "visible" } : {}} > - - + {/* */} + ) : null}
    @@ -192,12 +184,7 @@ const ToolTipLabel = ({ className={clsx("fa-layers fa-fw", classes.iconContainer)} style={showDescription ? { visibility: "visible" } : {}} > - - + ) : null}
    diff --git a/client/src/components/UI/AriaModal/ModalDialog.js b/client/src/components/UI/AriaModal/ModalDialog.js index 78f9fb93..cd3f685c 100644 --- a/client/src/components/UI/AriaModal/ModalDialog.js +++ b/client/src/components/UI/AriaModal/ModalDialog.js @@ -2,8 +2,7 @@ import React from "react"; import PropTypes from "prop-types"; import { createUseStyles } from "react-jss"; import AriaModal from "react-aria-modal"; -import { faX } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { FaX } from "react-icons/fa6"; const useStyles = createUseStyles({ modalContainer: { @@ -90,7 +89,7 @@ export default function ModalDialog({ className={classes.closeButton} aria-label={`Close ${title} modal`} > - +
    )} diff --git a/client/src/components/UI/Pagination.js b/client/src/components/UI/Pagination.js new file mode 100644 index 00000000..a2ad7dfa --- /dev/null +++ b/client/src/components/UI/Pagination.js @@ -0,0 +1,212 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Link } from "react-router-dom"; +import { createUseStyles } from "react-jss"; +import clsx from "clsx"; +import { MdChevronLeft, MdChevronRight } from "react-icons/md"; + +const useStyles = createUseStyles(theme => ({ + paginationContainer: { + marginRight: "10px" + }, + pagination: { + display: "flex" + }, + button: { + border: "none", + background: "none", + outline: "none", + width: "30px", + padding: "0", + margin: "0 5px" + }, + pageLinkContainer: { + height: "25px", + width: "25px", + display: "flex", + justifyContent: "center", + margin: "0 8px" + }, + pageLink: { + fontFamily: "Calibri", + fontWeight: "700", + margin: "auto 0", + fontSize: "18px", + textDecoration: "none", + padding: "0 0.5rem", + border: "none", + "&:hover": { + background: theme.colorDeselect + } + }, + currentPageLink: { + color: "white", + backgroundColor: "blue" + }, + dots: { + fontWeight: "400", + "&:hover": { + background: "none" + } + } +})); + +const Pagination = props => { + const { + projectsPerPage, + totalProjects, + paginate, + currentPage, + maxNumOfVisiblePages + } = props; + + const classes = useStyles(); + const pageNumbers = []; + let visiblePageLinks = []; + const totalNumOfPages = Math.ceil(totalProjects / projectsPerPage); + + for (let i = 1; i <= Math.ceil(totalProjects / projectsPerPage); i++) { + pageNumbers.push(i); + } + + const formatVisiblePages = pages => { + if (pages.length > maxNumOfVisiblePages) { + if (currentPage === pages[0]) { + pages.pop(); + } else { + pages.shift(); + } + } + return pages; + }; + + const calculateVisiblePageLinks = currentPage => { + let startPage, endPage; + + if (maxNumOfVisiblePages === 1) { + visiblePageLinks = [currentPage]; + return; + } + + if (totalNumOfPages <= maxNumOfVisiblePages) { + // Display all pages + startPage = 1; + endPage = totalNumOfPages; + } else if (currentPage < 3) { + // Case for first 2 pages + if (currentPage === 2) { + startPage = 1; + endPage = maxNumOfVisiblePages; + } else { + startPage = currentPage; + endPage = currentPage + (maxNumOfVisiblePages - 1); + } + } else if (currentPage + 2 >= totalNumOfPages) { + // Case for last 2 pages + startPage = totalNumOfPages - maxNumOfVisiblePages; + endPage = totalNumOfPages; + } else { + // Case for all other pages + startPage = currentPage - Math.floor(maxNumOfVisiblePages / 2); + endPage = currentPage + Math.floor(maxNumOfVisiblePages / 2); + } + + // push gathered page numbers into array + for (let i = startPage; i <= endPage; i++) { + visiblePageLinks.push(i); + } + + formatVisiblePages(visiblePageLinks); + return visiblePageLinks; + }; + + { + calculateVisiblePageLinks(currentPage); + } + + const displayPerimeterPages = (position, page) => { + let firstVisiblePage = visiblePageLinks[0]; + let lastVisiblePage = visiblePageLinks[visiblePageLinks.length - 1]; + const dots = ( + ... + ); + + const pageLinkItem = ( +
  • + {position === "right" ? dots : null} + paginate(page)} + > + {page} + + {position === "left" ? dots : null} +
  • + ); + + if (position === "left" && firstVisiblePage !== 1) { + return pageLinkItem; + } else if (position === "right" && lastVisiblePage !== totalNumOfPages) { + return pageLinkItem; + } + return false; + }; + + const leftPerimeterLink = displayPerimeterPages("left", 1); + const rightPerimeterLink = displayPerimeterPages("right", totalNumOfPages); + + return ( +
    +
      + + + {leftPerimeterLink} + + {visiblePageLinks.map(number => ( +
    • + paginate(number)} + > + {number} + +
    • + ))} + + {rightPerimeterLink} + + +
    +
    + ); +}; + +export default Pagination; + +Pagination.propTypes = { + projectsPerPage: PropTypes.number.isRequired, + totalProjects: PropTypes.number.isRequired, + paginate: PropTypes.func.isRequired, + currentPage: PropTypes.number.isRequired, + maxNumOfVisiblePages: PropTypes.number.isRequired +}; diff --git a/client/src/components/UI/RadioButton.jsx b/client/src/components/UI/RadioButton.jsx index 4d8847bd..e9fb0437 100644 --- a/client/src/components/UI/RadioButton.jsx +++ b/client/src/components/UI/RadioButton.jsx @@ -1,11 +1,17 @@ import React from "react"; import PropTypes from "prop-types"; -const RadioButton = ({ label, value, onChange }) => { +const RadioButton = ({ label, value, checked, onChange }) => { return ( -
    "; } + const msg = { to: laCityEmail, cc: forwardToWebTeam ? webTeamEmail : "", @@ -120,6 +134,7 @@ const sendFeedback = async (loginId, feedback) => { text: `TDM Feedback Submission - ${name}`, html: body }; + return sgMail.send(msg, false); } catch (err) { console.error(err); diff --git a/server/db/migration/V20240203.0923__add_config_table_and_sprocs_1502.sql b/server/db/migration/V20240203.0923__add_config_table_and_sprocs_1502.sql new file mode 100644 index 00000000..2ead04c7 --- /dev/null +++ b/server/db/migration/V20240203.0923__add_config_table_and_sprocs_1502.sql @@ -0,0 +1,102 @@ + +IF NOT EXISTS (SELECT * FROM SysObjects where name = 'Config' and xtype = 'U') + CREATE TABLE [dbo].[Config]( + [code] [nvarchar](250) NOT NULL, + [value] [nvarchar](max) NULL, + CONSTRAINT PK_Config PRIMARY KEY CLUSTERED (code) + ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +CREATE OR ALTER PROC Config_SelectAll +AS +BEGIN +/* + +EXEC Config_SelectAll + +*/ + +SELECT code, value +FROM Config + +END +GO + +CREATE OR ALTER PROC Config_Select +@code nvarchar(max) +AS +BEGIN +/* + +EXEC Config_Select 'Dummy' + +*/ + + SELECT code, value + FROM Config + WHERE code = @code + +END +GO + +CREATE OR ALTER PROC Config_Insert +@code nvarchar(max), +@value nvarchar(max) +AS +BEGIN +/* + +EXEC Config_Insert 'Dummy', 'A Test Value' + +*/ + +INSERT Config (code, value) +VALUES (@code, @value) + +END +GO + +CREATE OR ALTER PROC Config_Update +@code nvarchar(max), +@value nvarchar(max) +AS +BEGIN +/* + +EXEC Config_Update 'Dummy', 'A New Value' + +*/ + +UPDATE Config SET + value = @value +WHERE code = @code + +END +GO + +CREATE OR ALTER PROC Config_Delete +@code nvarchar(max) +AS +/* + EXEC Config_Delete 'Dummy' +*/ +BEGIN + + DELETE FROM Config + WHERE code = @code + +END +GO + +/* +Populate initially with Okta Development Settings. For other environments, the +Config values will need to be set manually or with another script. +*/ +IF NOT EXISTS (SELECT * FROM Config) +BEGIN + EXEC Config_Insert 'OKTA_ENABLE', 'T' + EXEC Config_Insert 'OKTA_CLIENT_ID', '0oaeecq7dzuXwy5go5d7' + EXEC Config_Insert 'OKTA_ISSUER', 'https://dev-50564150.okta.com/oauth2/default' +END +GO + diff --git a/server/db/migration/V20240204.2230__repopulate_faqcategory_table_1280.sql b/server/db/migration/V20240204.2230__repopulate_faqcategory_table_1280.sql new file mode 100644 index 00000000..eea9d43b --- /dev/null +++ b/server/db/migration/V20240204.2230__repopulate_faqcategory_table_1280.sql @@ -0,0 +1,19 @@ +DELETE FROM FaqCategory +GO + +SET IDENTITY_INSERT [dbo].[FaqCategory] ON +GO +INSERT [dbo].[FaqCategory] ([id], [name], [displayOrder], [faqs]) VALUES (1197, N'Important Updates', 20, N'[{"id":1,"question":"When will the TDM ordinance be effective?","answer":"

    The TDM Ordinance will take effect after it is adopted by the City Council and signed by the Mayor. You can track the steps of the legislative process via the City Clerk''s website (click on the envelope icon there to get email updates).

    Once the Ordinance becomes effective, there will be a phase-in period that will exempt certain projects that are already in the pipeline from the new provisions, including: projects that have submitted a complete application before the effective date; Level 2 and Level 3 Projects that receive a building permit within 180 days after the effective date; and Level 1 projects that receive a building permit within 365 days after the effective date of the Ordinance.

    ","faqCategoryId":7,"displayOrder":10},{"id":20,"question":"Do the hyperlinks work?","answer":"

    Here is an internal link.

    Here is an external link.

    ","faqCategoryId":1125,"displayOrder":20}]') +GO +INSERT [dbo].[FaqCategory] ([id], [name], [displayOrder], [faqs]) VALUES (1198, N'Getting Started', 30, N'[{"id":2,"question":"What do I need to prepare to get started? ","answer":"

    You may refer to the hyperlinked checklist at the bottom right of your browser window.

    ","faqCategoryId":14,"displayOrder":10},{"id":19,"question":"Does the TDM Ordinance apply to my project and do I need to use the TDM Calculator? ","answer":"

    Please check LAMC Section 12.26 J.3(c) of the Draft Revised TDM Ordinance for applicability and exemption details. If the TDM Ordinance applies to your project, you need to use the TDM Calculator to create a TDM Plan to submit to LADOT for review.

    ","faqCategoryId":14,"displayOrder":10}]') +GO +INSERT [dbo].[FaqCategory] ([id], [name], [displayOrder], [faqs]) VALUES (1199, N'Point System', 40, N'[{"id":3,"question":"How are Project Levels and Point Targets determined?","answer":"

    The Project Level is determined by the project use and size, and the Target Points are determined by a combination of the Project Level and amount of parking provided by the project (the percentage of parking in excess of the Citywide Parking Baseline). Learn more in Chapter 3 of the TDM Program Guidelines.

    ","faqCategoryId":15,"displayOrder":10},{"id":4,"question":"How are the point values (possible points) for each TDM Strategy determined?","answer":"

    Each TDM strategy''s assigned point value is based on its effectiveness at reducing VMT and how it advances the City''s policy goals. Each strategy is backed by academic research on how the measure changes travel behavior. Learn more in Chapter 3 of the TDM Program Guidelines.

    ","faqCategoryId":15,"displayOrder":10},{"id":5,"question":"What if the project''s Earned Points exceed the Target Points?","answer":"

    There is no benefit or penalty for selecting more TDM Strategies than are needed to meet the Target Points. Projects that exceed the Target Points are treated the same as projects that meet the Target Points exactly.

    ","faqCategoryId":15,"displayOrder":10},{"id":6,"question":"What if the project is not able to meet the Target Points?","answer":"

    An applicant must select enough TDM Strategies to meet the project''s required Point Target before submitting their TDM Plan to LADOT for review and approval. No building permit will be issued to any Project without an approved TDM Plan.

    ","faqCategoryId":15,"displayOrder":10}]') +GO +INSERT [dbo].[FaqCategory] ([id], [name], [displayOrder], [faqs]) VALUES (1200, N'Strategy Selection', 50, N'[{"id":7,"question":"Where can I read more about the definitions of the TDM Strategies?","answer":"

    You can find much of the information you will need on the \"Select TDM Strategies\" page of the TDM Calculator (page 4), by clicking on a Strategy to open an information box with more details. Learn more in Chapter 4 of the TDM Program Guidelines.

    ","faqCategoryId":16,"displayOrder":10},{"id":8,"question":"What is the User-Defined Strategy option for?","answer":"

    The User-Defined Strategy is to embrace effective innovations. You may propose one and apply for discretionary approval in order to receive points for strategies not currently in the menu of TDM Strategies. LADOT will review and give feedback about your proposal. Learn more in Chapter 4 of the TDM Program Guidelines.

    ","faqCategoryId":16,"displayOrder":10},{"id":10,"question":"What is a Bonus Package? How do I know whether my project qualifies?","answer":"

    A Bonus Package is a pre-packaged set of TDM Strategies to help you meet your project''s Point Target. If you choose the Bonus Package, the pre-packaged set of TDM Strategies will be selected, and the project will also earn one extra point. Bonus Packages are only available to Level 1 projects that do not provide parking in excess of the Citywide Parking Baseline. If your project qualifies, you will see the option at the top of the \"Select TDM Strategies\"; page of the TDM Calculator (page 4). Learn more about Bonus Packages in Chapter 4 of the TDM Program Guidelines.

    ","faqCategoryId":16,"displayOrder":10}]') +GO +INSERT [dbo].[FaqCategory] ([id], [name], [displayOrder], [faqs]) VALUES (1201, N'Relationship to Other Development Requirements', 60, N'[{"id":11,"question":"What are the differences between the TDM Calculator and the VMT Calculator?","answer":"

    The TDM program will apply to all projects that are above a threshold regardless if they need a discretionary action, while projects also need to evaluate VMT impacts if they require a discretionary action (also known as an entitlement) from the City. For projects that require CEQA analysis, some projects today need to reduce their VMT impact by applying TDM strategies as mitigation measures. For ease of use, once the TDM Ordinance update takes effect, applicants will want to complete the TDM Calculator first ensure compliance, and determine if any of the TDM strategies they selected will help to include as VMT mitigation measures in the VMT Calculator if they found any impacts during the CEQA process. When the TDM Ordinance update takes effect, developers may claim TDM strategies as regulatory compliance measures (RCMs) that are required to comply with the TDM Program, and thereby demonstrate a lower VMT when evaluating their project''s CEQA analysis. This will allow some projects to reduce VMT through the design and features of the project, creating opportunities for CEQA streamlining.

    ","faqCategoryId":17,"displayOrder":10}]') +GO +INSERT [dbo].[FaqCategory] ([id], [name], [displayOrder], [faqs]) VALUES (1202, N'Next Steps', 70, N'[{"id":12,"question":"What do I do with the TDM Calculatorā€™s summary information?","answer":"

    This will be addressed when the ordinance is approved and the LADOT is ready for submissions.

    ","faqCategoryId":18,"displayOrder":10},{"id":13,"question":"What other information is required to submit with my TDM Plan for TDM Ordinance compliance?","answer":"

    Project applicants will submit all the usual documents required to obtain Planning Entitlements (if needed) and Building Permits for the proposed project. Contact the Development Services Center with general questions.

    For TDM Ordinance compliance specifically, applicants will submit the TDM Plan, site and building plans, other relevant documentation of the proposed TDM Strategies (contracts, memberships, pricing information, etc, if applicable), and development review fees to LADOT for review and approval. Final documentation (such as contracts) must be submitted, a covenant and agreement must be recorded, and a building inspection must be completed before a Certificate of Occupancy will be issued.

    ","faqCategoryId":18,"displayOrder":10},{"id":14,"question":"What is the approval process after submitting my TDM Plan to LADOT?","answer":"

    LADOT will review your project''s proposed TDM Plan within 30 days in a ministerial process if you do not select any measures that require pre-approval from another agency. Building a new Bike Share station, for example, will require pre-approval from Metro and other LA City agencies. In the case that your project has TDM strategies that require such pre-approval, LADOT will review your project''s proposed TDM plan within 90 days.

    Your project needs an approved TDM Plan before building permits will be issued. Your project needs to record a covenant stating the TDM Plan will be maintained for the lifetime of the Project before a Certificate of Occupancy will be issued.

    ","faqCategoryId":18,"displayOrder":10},{"id":15,"question":"What does my project need to do for annual compliance with TDM?","answer":"

    After your project receives its Certificate of Occupancy, you will need to submit annual compliance documentation to LADOT. Required documentation may range from a dated photograph of a project feature to copies of receipts and contracts. Level 3 Projects will also need to send monitoring data to LADOT on an annual basis, which involves surveying workers and residents on site about their travel behavior. LADOT will release more information about required compliance documentation and monitoring standards prior to the ordinance effective date.

    ","faqCategoryId":18,"displayOrder":10},{"id":16,"question":"How can I view, edit, and manage my past and/or current projects?","answer":"

    If you log in to the TDM Calculator, you will see a My Projects tab at the top left of the page. In this section, you can view all of your saved projects and copy, open, edit, and manage them from here.

    ","faqCategoryId":18,"displayOrder":10},{"id":17,"question":"Canā€™t find your answers here?","answer":"

    Please refer to the TDM Program Guidelines for details about the TDM Program and Strategies. If the Guidelines doesnā€™t answer your specific questions, you may also reach us at our feedback page for further assistance.

    ","faqCategoryId":18,"displayOrder":10}]') +GO +SET IDENTITY_INSERT [dbo].[FaqCategory] OFF +GO \ No newline at end of file diff --git a/server/db/migration/V20240501.1524__.changes_to_csv_format_1679.sql b/server/db/migration/V20240501.1524__.changes_to_csv_format_1679.sql new file mode 100644 index 00000000..263f118e --- /dev/null +++ b/server/db/migration/V20240501.1524__.changes_to_csv_format_1679.sql @@ -0,0 +1,3 @@ +update CalculationRule SET + dataType = 'number' +where calculationId = 1 and code = 'PTS_PKG_RESIDENTIAL_COMMERCIAL' \ No newline at end of file diff --git a/server/db/migration/V20240509.1230__remove_obsoloete_faq_table_and_sprocs_1605.sql b/server/db/migration/V20240509.1230__remove_obsoloete_faq_table_and_sprocs_1605.sql new file mode 100644 index 00000000..d982a812 --- /dev/null +++ b/server/db/migration/V20240509.1230__remove_obsoloete_faq_table_and_sprocs_1605.sql @@ -0,0 +1,7 @@ +drop proc if exists Faq_Insert; +drop proc if exists Faq_Update; +drop proc if exists Faq_Delete; +drop proc if exists Faq_SelectAll; +drop proc if exists Faq_SelectById; + +drop table if exists Faq; \ No newline at end of file diff --git a/server/db/migration/V20240529.1948__add_dateSubmitted_column_1704.sql b/server/db/migration/V20240529.1948__add_dateSubmitted_column_1704.sql new file mode 100644 index 00000000..32285571 --- /dev/null +++ b/server/db/migration/V20240529.1948__add_dateSubmitted_column_1704.sql @@ -0,0 +1 @@ +ALTER TABLE Project ADD dateSubmitted datetime2(0) NULL; \ No newline at end of file diff --git a/server/db/migration/V20240619.1716__remove_strategy_info_5_1737.sql b/server/db/migration/V20240619.1716__remove_strategy_info_5_1737.sql new file mode 100644 index 00000000..ed1ef3d4 --- /dev/null +++ b/server/db/migration/V20240619.1716__remove_strategy_info_5_1737.sql @@ -0,0 +1,18 @@ +if exists (SELECT * from calculationrule where calculationid = 1 and code = 'STRATEGY_INFO_5') +begin + delete calculationrule where calculationid = 1 and code = 'STRATEGY_INFO_5'; +end + +if exists (SELECT * from calculationrule where calculationid = 1 and code = 'PTS_INFO_5') + +begin + delete calculationrule where calculationid = 1 and code = 'PTS_INFO_5'; +end + +update calculationrule set +functionBody = ' return (<> === 1 && !!<> && <> && (<> >= 2) && !!(<> >= 2)) ? 1 : 0; ' +where calculationid = 1 and code = 'PTS_PKG_SCHOOL' + +update calculationrule set +functionBody = 'return <> + <> + <>;' +where calculationid = 1 and code = 'PTS_INFO' diff --git a/server/db/migration/V20240626.1243__add_datesubmitted_col_to_project_select_sprocs_1759.sql b/server/db/migration/V20240626.1243__add_datesubmitted_col_to_project_select_sprocs_1759.sql new file mode 100644 index 00000000..8a201b3c --- /dev/null +++ b/server/db/migration/V20240626.1243__add_datesubmitted_col_to_project_select_sprocs_1759.sql @@ -0,0 +1,196 @@ +/****** Object: StoredProcedure [dbo].[Project_SelectAll] Script Date: 6/26/2024 12:41:27 PM ******/ +DROP PROCEDURE [dbo].[Project_SelectAll] +GO + +/****** Object: StoredProcedure [dbo].[Project_SelectAll] Script Date: 6/26/2024 12:41:27 PM ******/ +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER ON +GO + +-- alters sproc so that the "getAll projects" filters out archived projects +CREATE PROC [dbo].[Project_SelectAll] + @loginId int = null +AS +BEGIN + + /* + // LADOT (i.e., Admin) user sees all projects + EXEC dbo.Project_SelectAll @loginId = 37 + + // Regular user sees only his/her projects + EXEC dbo.Project_SelectAll @loginId = 11 + +*/ +IF EXISTS(SELECT 1 + FROM Login + WHERE id = @LoginId and isAdmin = 1) + BEGIN + -- Admin can see all projects, but not the archived ones + SELECT + p.id + , p.name + , p.address + , p.formInputs + , p.loginId + , p.calculationId + , p.dateCreated + , p.dateModified + , p.description + , author.firstName + , author.lastName + , p.dateHidden + , p.dateTrashed + , p.dateSnapshotted + , p.dateSubmitted + FROM Project p + JOIN Login author on p.loginId = author.id + WHERE p.archivedAt IS NULL + END + ELSE + BEGIN + -- User can only see their own projects + SELECT + p.id + , p.name + , p.address + , p.formInputs + , p.loginId + , p.calculationId + , p.dateCreated + , p.dateModified + , p.description + , author.firstName + , author.lastName + , p.dateHidden + , p.dateTrashed + , p.dateSnapshotted + , p.dateSubmitted + FROM Project p + JOIN Login author on p.loginId = author.id + WHERE author.id = ISNULL(@loginId, author.id) + END + +END +GO + + +/****** Object: StoredProcedure [dbo].[Project_SelectAllArchived] Script Date: 6/26/2024 12:42:25 PM ******/ +DROP PROCEDURE [dbo].[Project_SelectAllArchived] +GO + +/****** Object: StoredProcedure [dbo].[Project_SelectAllArchived] Script Date: 6/26/2024 12:42:25 PM ******/ +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER ON +GO + +-- adds sproc for getting all archived projects +CREATE PROCEDURE [dbo].[Project_SelectAllArchived] +AS +BEGIN + SELECT + p.id + , p.name + , p.address + , p.formInputs + , p.loginId + , p.calculationId + , p.dateCreated + , p.dateModified + , p.description + , author.firstName + , author.lastName + , author.email + , p.dateHidden + , p.dateTrashed + , p.dateSnapshotted + , p.archivedAt + , p.dateSubmitted + FROM Project p + JOIN Login author on p.loginId = author.id + WHERE p.archivedAt IS NOT NULL +END; +GO + +/****** Object: StoredProcedure [dbo].[Project_SelectById] Script Date: 6/26/2024 12:42:52 PM ******/ +DROP PROCEDURE [dbo].[Project_SelectById] +GO + +/****** Object: StoredProcedure [dbo].[Project_SelectById] Script Date: 6/26/2024 12:42:52 PM ******/ +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER ON +GO + +CREATE PROC [dbo].[Project_SelectById] + @loginId int = null, + @id int +AS +BEGIN + /* + + EXEC dbo.Project_SelectById @loginId = 37, @id = 2 + + EXEC dbo.Project_SelectById @loginId = 11, @id = 2 + +*/ + + IF EXISTS(SELECT 1 + FROM Login + WHERE id = @LoginId and isAdmin = 1) + BEGIN + SELECT + p.id + , p.name + , p.address + , p.formInputs + , p.loginId + , p.calculationId + , p.dateCreated + , p.dateModified + , p.description + , l.firstName + , l.lastName + , p.dateHidden + , p.dateTrashed + , p.dateSnapshotted + , p.dateSubmitted + FROM Project p + JOIN Login l on p.loginId = l.id + WHERE + p.id = @id + END + ELSE + BEGIN + SELECT + p.id + , p.name + , p.address + , p.formInputs + , p.loginId + , p.calculationId + , p.dateCreated + , p.dateModified + , p.description + , l.firstName + , l.lastName + , p.dateHidden + , p.dateTrashed + , p.dateSnapshotted + , p.dateSubmitted + FROM Project p + JOIN Login l on p.loginId = l.id + WHERE + p.id = @id + AND p.loginId = ISNULL(@loginId, p.loginId) + END + +END +GO + + + diff --git a/server/middleware/jwt-session.js b/server/middleware/jwt-session.js index 5f0c3ae3..20e24807 100644 --- a/server/middleware/jwt-session.js +++ b/server/middleware/jwt-session.js @@ -88,10 +88,10 @@ const validateRoles = authorizedRoles => ) { return next(); } - res.status("403").send("Unauthorized request"); + res.status(403).send("Unauthorized request"); } } catch (er) { - res.status("401").send("Unauthenticated User"); + res.status(401).send("Unauthenticated User"); } }; diff --git a/server/package-lock.json b/server/package-lock.json index 5951826c..31040647 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "tdm-calculator-api", - "version": "0.2.48", + "version": "0.2.50", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "tdm-calculator-api", - "version": "0.2.48", + "version": "0.2.50", "license": "ISC", "dependencies": { "@sendgrid/mail": "^8.1.0", @@ -15,16 +15,16 @@ "cors": "^2.8.5", "dotenv": "^16.3.1", "error-handler": "^1.0.0", - "express": "^4.18.1", + "express": "^4.19.2", "express-json-validator-middleware": "^3.0.1", "express-pino-logger": "^7.0.0", "jsonwebtoken": "^9.0.2", - "moment": "^2.29.3", - "mssql": "^10.0.1", + "mssql": "^11.0.1", "node-flywaydb": "^3.0.7", "path": "^0.12.7" }, "devDependencies": { + "@testcontainers/mssqlserver": "^10.9.0", "eslint": "^8.53.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-jest": "^27.6.0", @@ -60,115 +60,117 @@ } }, "node_modules/@azure/abort-controller": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", - "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", "dependencies": { - "tslib": "^2.2.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" } }, "node_modules/@azure/core-auth": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.5.0.tgz", - "integrity": "sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.7.2.tgz", + "integrity": "sha512-Igm/S3fDYmnMq1uKS38Ae1/m37B3zigdlZw+kocwEhh5GjyKjPrXKO2J6rzpC1wAxrNil/jX9BJRqBshyjnF3g==", "dependencies": { - "@azure/abort-controller": "^1.0.0", + "@azure/abort-controller": "^2.0.0", "@azure/core-util": "^1.1.0", - "tslib": "^2.2.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" } }, "node_modules/@azure/core-client": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.7.3.tgz", - "integrity": "sha512-kleJ1iUTxcO32Y06dH9Pfi9K4U+Tlb111WXEnbt7R/ne+NLRwppZiTGJuTD5VVoxTMK5NTbEtm5t2vcdNCFe2g==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", + "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", "dependencies": { - "@azure/abort-controller": "^1.0.0", + "@azure/abort-controller": "^2.0.0", "@azure/core-auth": "^1.4.0", "@azure/core-rest-pipeline": "^1.9.1", "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.0.0", + "@azure/core-util": "^1.6.1", "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" } }, "node_modules/@azure/core-http-compat": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-1.3.0.tgz", - "integrity": "sha512-ZN9avruqbQ5TxopzG3ih3KRy52n8OAbitX3fnZT5go4hzu0J+KVPSzkL+Wt3hpJpdG8WIfg1sBD1tWkgUdEpBA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.1.2.tgz", + "integrity": "sha512-5MnV1yqzZwgNLLjlizsU3QqOeQChkIXw781Fwh1xdAqJR5AA32IUaq6xv1BICJvfbHoa+JYcaij2HFkhLbNTJQ==", "dependencies": { - "@azure/abort-controller": "^1.0.4", + "@azure/abort-controller": "^2.0.0", "@azure/core-client": "^1.3.0", "@azure/core-rest-pipeline": "^1.3.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" } }, "node_modules/@azure/core-lro": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.4.tgz", - "integrity": "sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", + "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", "dependencies": { - "@azure/abort-controller": "^1.0.0", + "@azure/abort-controller": "^2.0.0", "@azure/core-util": "^1.2.0", "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" } }, "node_modules/@azure/core-paging": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz", - "integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", + "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", "dependencies": { - "tslib": "^2.2.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" } }, "node_modules/@azure/core-rest-pipeline": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.12.2.tgz", - "integrity": "sha512-wLLJQdL4v1yoqYtEtjKNjf8pJ/G/BqVomAWxcKOR1KbZJyCEnCv04yks7Y1NhJ3JzxbDs307W67uX0JzklFdCg==", + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.16.3.tgz", + "integrity": "sha512-VxLk4AHLyqcHsfKe4MZ6IQ+D+ShuByy+RfStKfSjxJoL3WBWq17VNmrz8aT8etKzqc2nAeIyLxScjpzsS4fz8w==", "dependencies": { - "@azure/abort-controller": "^1.0.0", + "@azure/abort-controller": "^2.0.0", "@azure/core-auth": "^1.4.0", "@azure/core-tracing": "^1.0.1", - "@azure/core-util": "^1.3.0", + "@azure/core-util": "^1.9.0", "@azure/logger": "^1.0.0", - "form-data": "^4.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "tslib": "^2.2.0" + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/@azure/core-rest-pipeline/node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "node_modules/@azure/core-rest-pipeline/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dependencies": { + "debug": "^4.3.4" + }, "engines": { - "node": ">= 10" + "node": ">= 14" } }, "node_modules/@azure/core-rest-pipeline/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", "dependencies": { "ms": "2.1.2" }, @@ -182,16 +184,27 @@ } }, "node_modules/@azure/core-rest-pipeline/node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@azure/core-rest-pipeline/node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dependencies": { + "agent-base": "^7.0.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/@azure/core-rest-pipeline/node_modules/ms": { @@ -200,42 +213,42 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@azure/core-tracing": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz", - "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz", + "integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==", "dependencies": { - "tslib": "^2.2.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" } }, "node_modules/@azure/core-util": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.6.1.tgz", - "integrity": "sha512-h5taHeySlsV9qxuK64KZxy4iln1BtMYlNt5jbuEFN3UFSAd1EwKg/Gjl5a6tZ/W8t6li3xPnutOx7zbDyXnPmQ==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.9.2.tgz", + "integrity": "sha512-l1Qrqhi4x1aekkV+OlcqsJa4AnAkj5p0JV8omgwjaV9OAbP41lvrMvs+CptfetKkeEaGRGSzby7sjPZEX7+kkQ==", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "tslib": "^2.2.0" + "@azure/abort-controller": "^2.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@azure/identity": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-3.4.1.tgz", - "integrity": "sha512-oQ/r5MBdfZTMIUcY5Ch8G7Vv9aIXDkEYyU4Dfqjim4MQN+LY2uiQ57P1JDopMLeHCsZxM4yy8lEdne3tM9Xhzg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.4.1.tgz", + "integrity": "sha512-DwnG4cKFEM7S3T+9u05NstXU/HN0dk45kPOinUyNKsn5VWwpXd9sbPKEg6kgJzGbm1lMuhx9o31PVbCtM5sfBA==", "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-auth": "^1.5.0", - "@azure/core-client": "^1.4.0", + "@azure/core-client": "^1.9.2", "@azure/core-rest-pipeline": "^1.1.0", "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.6.1", + "@azure/core-util": "^1.3.0", "@azure/logger": "^1.0.0", - "@azure/msal-browser": "^3.5.0", - "@azure/msal-node": "^2.5.1", + "@azure/msal-browser": "^3.14.0", + "@azure/msal-node": "^2.9.2", "events": "^3.0.0", "jws": "^4.0.0", "open": "^8.0.0", @@ -243,7 +256,18 @@ "tslib": "^2.2.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/@azure/identity/node_modules/@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" } }, "node_modules/@azure/identity/node_modules/jwa": { @@ -266,14 +290,14 @@ } }, "node_modules/@azure/keyvault-keys": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.7.2.tgz", - "integrity": "sha512-VdIH6PjbQ3J5ntK+xeI8eOe1WsDxF9ndXw8BPR/9MZVnIj0vQNtNCS6gpR7EFQeGcs8XjzMfHm0AvKGErobqJQ==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.8.0.tgz", + "integrity": "sha512-jkuYxgkw0aaRfk40OQhFqDIupqblIOIlYESWB6DKCVDxQet1pyv86Tfk9M+5uFM0+mCs6+MUHU+Hxh3joiUn4Q==", "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-auth": "^1.3.0", "@azure/core-client": "^1.5.0", - "@azure/core-http-compat": "^1.3.0", + "@azure/core-http-compat": "^2.0.1", "@azure/core-lro": "^2.2.0", "@azure/core-paging": "^1.1.1", "@azure/core-rest-pipeline": "^1.8.1", @@ -283,50 +307,61 @@ "tslib": "^2.2.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" } }, - "node_modules/@azure/logger": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", - "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", + "node_modules/@azure/keyvault-keys/node_modules/@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", "dependencies": { "tslib": "^2.2.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=12.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.4.tgz", + "integrity": "sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@azure/msal-browser": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.6.0.tgz", - "integrity": "sha512-FrFBJXRJMyWXjAjg4cUNZwEKktzfzD/YD9+S1kj2ors67hKoveam4aL0bZuCZU/jTiHTn0xDQGQh2ksCMXTXtA==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.20.0.tgz", + "integrity": "sha512-ErsxbfCGIwdqD8jipqdxpfAGiUEQS7MWUe39Rjhl0ZVPsb1JEe9bZCe2+0g23HDH6DGyCAtnTNN9scPtievrMQ==", "dependencies": { - "@azure/msal-common": "14.5.0" + "@azure/msal-common": "14.14.0" }, "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-common": { - "version": "14.5.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.5.0.tgz", - "integrity": "sha512-Gx5rZbiZV/HiZ2nEKfjfAF/qDdZ4/QWxMvMo2jhIFVz528dVKtaZyFAOtsX2Ak8+TQvRsGCaEfuwJFuXB6tu1A==", + "version": "14.14.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.14.0.tgz", + "integrity": "sha512-OxcOk9H1/1fktHh6//VCORgSNJc2dCQObTm6JNmL824Z6iZSO6eFo/Bttxe0hETn9B+cr7gDouTQtsRq3YPuSQ==", "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-node": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.6.0.tgz", - "integrity": "sha512-RWAWCYYrSldIYC47oWtofIun41e6SB9TBYgGYsezq6ednagwo9ZRFyRsvl1NabmdTkdDDXRAABIdveeN2Gtd8w==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.12.0.tgz", + "integrity": "sha512-jmk5Im5KujRA2AcyCb0awA3buV8niSrwXZs+NBJWIvxOz76RvNlusGIqi43A0h45BPUy93Qb+CPdpJn82NFTIg==", "dependencies": { - "@azure/msal-common": "14.5.0", + "@azure/msal-common": "14.14.0", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, "engines": { - "node": "16|| 18 || 20" + "node": ">=16" } }, "node_modules/@babel/code-frame": { @@ -1614,9 +1649,9 @@ } }, "node_modules/@js-joda/core": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.6.1.tgz", - "integrity": "sha512-Xla/d7ZMMR6+zRd6lTio0wRZECfcfFJP7GGe9A9L4tDOlD5CX4YcZ4YZle9w58bBYzssojVapI84RraKWDQZRg==" + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.6.3.tgz", + "integrity": "sha512-T1rRxzdqkEXcou0ZprN1q9yDRlvzCPLqmlNt5IIsGBzoEVgLCCYrKEwc84+TvsXuAc95VAZwtWD2zVsKPY4bcA==" }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", @@ -1783,6 +1818,15 @@ "resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.5.0.tgz", "integrity": "sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ==" }, + "node_modules/@testcontainers/mssqlserver": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@testcontainers/mssqlserver/-/mssqlserver-10.9.0.tgz", + "integrity": "sha512-ODKdjGKsiqbluCldHvEijuuT7h+q/2+J+vCP2Na//2815FBx6OuRmoAYD4mwMEKvBlBL5HYWrkXUpQpTm8x84g==", + "dev": true, + "dependencies": { + "testcontainers": "^10.9.0" + } + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "license": "MIT", @@ -1846,23 +1890,47 @@ "@types/node": "*" } }, + "node_modules/@types/docker-modem": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", + "integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@types/dockerode": { + "version": "3.3.29", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.29.tgz", + "integrity": "sha512-5PRRq/yt5OT/Jf77ltIdz4EiR9+VLnPF+HpU4xGFwUqmV24Co2HKBNW3w+slqZ1CYchbcDeqJASHDYWzZCcMiQ==", + "dev": true, + "dependencies": { + "@types/docker-modem": "*", + "@types/node": "*", + "@types/ssh2": "*" + } + }, "node_modules/@types/express": { - "version": "4.17.13", - "license": "MIT", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", + "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.29", - "license": "MIT", + "version": "4.17.42", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.42.tgz", + "integrity": "sha512-ckM3jm2bf/MfB3+spLPWYPUH573plBFwpOhqQ2WottxYV85j1HQFlxmnTq57X1yHY9awZPig06hL/cLMgNWHIQ==", "dependencies": { "@types/node": "*", "@types/qs": "*", - "@types/range-parser": "*" + "@types/range-parser": "*", + "@types/send": "*" } }, "node_modules/@types/graceful-fs": { @@ -1907,8 +1975,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "17.0.33", - "license": "MIT" + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", + "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", + "dependencies": { + "undici-types": "~6.13.0" + } }, "node_modules/@types/qs": { "version": "6.9.7", @@ -1918,6 +1990,29 @@ "version": "1.2.4", "license": "MIT" }, + "node_modules/@types/readable-stream": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.15.tgz", + "integrity": "sha512-oAZ3kw+kJFkEqyh7xORZOku1YAKvsFTogRY8kVl4vHpEKiDkfnSA/My8haRE7fvmix5Zyy+1pwzOi7yycGLBJw==", + "dependencies": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "node_modules/@types/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==" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "node_modules/@types/serve-static": { "version": "1.13.10", "license": "MIT", @@ -2351,18 +2446,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/array-flatten": { "version": "1.1.1", "license": "MIT" @@ -2375,26 +2458,6 @@ "node": ">=8" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/asap": { "version": "2.0.6", "dev": true, @@ -2414,9 +2477,10 @@ "license": "MIT" }, "node_modules/async-lock": { - "version": "1.4.0", - "dev": true, - "license": "MIT" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "dev": true }, "node_modules/asynckit": { "version": "0.4.0", @@ -2429,17 +2493,6 @@ "node": ">=8.0.0" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/axios": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", @@ -2451,9 +2504,10 @@ } }, "node_modules/b4a": { - "version": "1.6.4", - "dev": true, - "license": "ISC" + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", + "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", + "dev": true }, "node_modules/babel-jest": { "version": "29.7.0", @@ -2575,6 +2629,52 @@ "version": "1.0.2", "license": "MIT" }, + "node_modules/bare-events": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.2.tgz", + "integrity": "sha512-h7z00dWdG0PYOQEvChhOSWvOfkIKsdZGkWr083FgN/HyoQuebSew/cgirYqh9SCuy/hRvxc5Vy6Fw8xAmYHLkQ==", + "dev": true, + "optional": true + }, + "node_modules/bare-fs": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.0.tgz", + "integrity": "sha512-TNFqa1B4N99pds2a5NYHR15o0ZpdNKbAeKTE/+G6ED/UeOavv8RY3dr/Fu99HW3zU3pXpo2kDNO8Sjsm2esfOw==", + "dev": true, + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^1.0.0" + } + }, + "node_modules/bare-os": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.3.0.tgz", + "integrity": "sha512-oPb8oMM1xZbhRQBngTgpcQ5gXw6kjOaRsSWsIeNyRxGed2w/ARyP7ScBYpWR1qfX2E5rS3gBw6OWcSQo+s+kUg==", + "dev": true, + "optional": true + }, + "node_modules/bare-path": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.2.tgz", + "integrity": "sha512-o7KSt4prEphWUHa3QUwCxUI00R86VdjiuxmJK0iNVDHYPGo+HsDaVCnqCmPbf/MiW1ok8F4p3m8RTHlWk8K2ig==", + "dev": true, + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, + "node_modules/bare-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-1.0.0.tgz", + "integrity": "sha512-KhNUoDL40iP4gFaLSsoGE479t0jHijfYdIcxRn/XtezA2BaUD0NRf/JGRpsMq6dMNM+SrCrB0YSSo/5wBY4rOQ==", + "dev": true, + "optional": true, + "dependencies": { + "streamx": "^2.16.1" + } + }, "node_modules/base64-js": { "version": "1.5.1", "funding": [ @@ -2632,19 +2732,20 @@ } }, "node_modules/bl": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.9.tgz", - "integrity": "sha512-Vh+M9HMfeTST9rkkQ1utRnOeABNcBO3i0dJMFkenCv7JIp76XWx8uQOGpaXyXVyenrLDZsdAHXbf0Cz18Eb0fw==", + "version": "6.0.14", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.14.tgz", + "integrity": "sha512-TJfbvGdL7KFGxTsEbsED7avqpFdY56q9IW0/aiytyheJzxST/+Io6cx/4Qx0K2/u0BPRDs65mjaQzYvMZeNocQ==", "dependencies": { + "@types/readable-stream": "^4.0.0", "buffer": "^6.0.3", "inherits": "^2.0.4", "readable-stream": "^4.2.0" } }, "node_modules/bl/node_modules/readable-stream": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz", - "integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", @@ -2657,19 +2758,20 @@ } }, "node_modules/body-parser": { - "version": "1.20.0", - "license": "MIT", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.10.3", - "raw-body": "2.5.1", + "qs": "6.11.0", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -2699,11 +2801,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -3144,8 +3247,9 @@ } }, "node_modules/content-type": { - "version": "1.0.4", - "license": "MIT", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { "node": ">= 0.6" } @@ -3456,22 +3560,6 @@ "node": ">=8" } }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "license": "MIT", @@ -3546,9 +3634,10 @@ } }, "node_modules/docker-compose": { - "version": "0.24.2", + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.8.tgz", + "integrity": "sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==", "dev": true, - "license": "MIT", "dependencies": { "yaml": "^2.2.2" }, @@ -3720,108 +3809,6 @@ "version": "1.0.0", "license": "MIT" }, - "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-aggregate-error": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.11.tgz", - "integrity": "sha512-DCiZiNlMlbvofET/cE55My387NiLvuGToBEZDdK9U2G3svDCjL8WOgO5Il6lO83nQ8qmag/R9nArdpaFQ/m3lA==", - "dependencies": { - "define-data-property": "^1.1.0", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "set-function-name": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", - "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -4199,15 +4186,16 @@ } }, "node_modules/express": { - "version": "4.18.1", - "license": "MIT", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.0", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -4223,7 +4211,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.10.3", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", @@ -4277,8 +4265,9 @@ } }, "node_modules/express/node_modules/cookie": { - "version": "0.5.0", - "license": "MIT", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } @@ -4344,8 +4333,9 @@ }, "node_modules/fast-fifo": { "version": "1.3.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true }, "node_modules/fast-glob": { "version": "3.3.2", @@ -4430,9 +4420,10 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -4490,9 +4481,9 @@ "license": "ISC" }, "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -4508,14 +4499,6 @@ } } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dependencies": { - "is-callable": "^1.1.3" - } - }, "node_modules/form-data": { "version": "4.0.0", "license": "MIT", @@ -4620,31 +4603,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", @@ -4752,21 +4710,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/glob": { "version": "7.2.3", "license": "ISC", @@ -4811,20 +4754,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/globby": { "version": "11.1.0", "dev": true, @@ -4866,14 +4795,6 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-flag": { "version": "4.0.0", "dev": true, @@ -4914,20 +4835,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -5138,19 +5045,6 @@ "version": "2.0.4", "license": "ISC" }, - "node_modules/internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", - "dependencies": { - "get-intrinsic": "^1.2.2", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/ipaddr.js": { "version": "1.9.1", "license": "MIT", @@ -5158,36 +5052,12 @@ "node": ">= 0.10" } }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-binary-path": { "version": "2.1.0", "dev": true, @@ -5199,32 +5069,6 @@ "node": ">=8" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-core-module": { "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", @@ -5237,20 +5081,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-docker": { "version": "2.2.1", "license": "MIT", @@ -5337,39 +5167,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -5379,32 +5185,6 @@ "node": ">=8" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -5417,59 +5197,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dependencies": { - "which-typed-array": "^1.1.11" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-wsl": { "version": "2.2.0", "license": "MIT", @@ -6172,11 +5899,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbi": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.3.0.tgz", - "integrity": "sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==" - }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -6879,34 +6601,27 @@ "dev": true, "license": "MIT" }, - "node_modules/moment": { - "version": "2.29.4", - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/ms": { "version": "2.0.0", "license": "MIT" }, "node_modules/mssql": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/mssql/-/mssql-10.0.1.tgz", - "integrity": "sha512-k0Xkav/3OppZs8Kj+FIo7k7ejbcsVNxp5/ePayxfXzuBZhxD/Y/RhIhrtfHyH6FmlJnBQPj7eDI2IN7B0BiSxQ==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/mssql/-/mssql-11.0.1.tgz", + "integrity": "sha512-KlGNsugoT90enKlR8/G36H0kTxPthDhmtNUCwEHvgRza5Cjpjoj+P2X6eMpFUDN7pFrJZsKadL4x990G8RBE1w==", "dependencies": { "@tediousjs/connection-string": "^0.5.0", "commander": "^11.0.0", "debug": "^4.3.3", "rfdc": "^1.3.0", "tarn": "^3.0.2", - "tedious": "^16.4.0" + "tedious": "^18.2.1" }, "bin": { "mssql": "bin/mssql" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/mssql/node_modules/debug": { @@ -6951,11 +6666,6 @@ "node": ">= 0.6" } }, - "node_modules/node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" - }, "node_modules/node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", @@ -7149,31 +6859,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/on-exit-leak-free": { "version": "0.2.0", "license": "MIT" @@ -7686,8 +7371,9 @@ ] }, "node_modules/qs": { - "version": "6.10.3", - "license": "BSD-3-Clause", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dependencies": { "side-channel": "^1.0.4" }, @@ -7719,8 +7405,9 @@ }, "node_modules/queue-tick": { "version": "1.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true }, "node_modules/quick-format-unescaped": { "version": "4.0.4", @@ -7734,8 +7421,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "license": "MIT", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -7809,22 +7497,6 @@ "node": ">= 12.13.0" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7984,28 +7656,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-array-concat/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - }, "node_modules/safe-buffer": { "version": "5.2.1", "funding": [ @@ -8024,19 +7674,6 @@ ], "license": "MIT" }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/safe-stable-stringify": { "version": "2.3.1", "license": "MIT", @@ -8123,19 +7760,6 @@ "node": ">= 0.4" } }, - "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", - "dependencies": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/setprototypeof": { "version": "1.2.0", "license": "ISC" @@ -8340,12 +7964,16 @@ "license": "MIT" }, "node_modules/streamx": { - "version": "2.15.1", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.16.1.tgz", + "integrity": "sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==", "dev": true, - "license": "MIT", "dependencies": { "fast-fifo": "^1.1.0", "queue-tick": "^1.0.1" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" } }, "node_modules/string_decoder": { @@ -8421,48 +8049,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/strip-ansi": { "version": "6.0.1", "license": "MIT", @@ -8621,9 +8207,9 @@ } }, "node_modules/tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -8637,19 +8223,24 @@ } }, "node_modules/tar-fs": { - "version": "3.0.4", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", + "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", "dev": true, - "license": "MIT", "dependencies": { - "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" } }, "node_modules/tar-fs/node_modules/tar-stream": { - "version": "3.1.6", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dev": true, - "license": "MIT", "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", @@ -8712,25 +8303,23 @@ } }, "node_modules/tedious": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/tedious/-/tedious-16.6.1.tgz", - "integrity": "sha512-KKSDB1OPrPk0WbMPug9YqRbPl44zMjdL2hFyzLEidr2IkItzpV0ZbzW8VA47QIS2oyWhCU7ifIEQY12n23IRDA==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/tedious/-/tedious-18.3.0.tgz", + "integrity": "sha512-/aVCX2VLu9Ihf5UyxifRXfmWQ1P8HNJvapk1as+LMzSdw9AmbNtEHllrffZpNrzBCptcK0Z4m06k+tutL2wowA==", "dependencies": { - "@azure/identity": "^3.4.1", + "@azure/core-auth": "^1.7.2", + "@azure/identity": "^4.2.1", "@azure/keyvault-keys": "^4.4.0", - "@js-joda/core": "^5.5.3", - "bl": "^6.0.3", - "es-aggregate-error": "^1.0.9", + "@js-joda/core": "^5.6.1", + "@types/node": ">=18", + "bl": "^6.0.11", "iconv-lite": "^0.6.3", "js-md4": "^0.3.2", - "jsbi": "^4.3.0", "native-duplexpair": "^1.0.0", - "node-abort-controller": "^3.1.1", - "punycode": "^2.3.0", - "sprintf-js": "^1.1.2" + "sprintf-js": "^1.1.3" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/tedious/node_modules/iconv-lite": { @@ -8744,14 +8333,6 @@ "node": ">=0.10.0" } }, - "node_modules/tedious/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "engines": { - "node": ">=6" - } - }, "node_modules/tedious/node_modules/sprintf-js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", @@ -8772,23 +8353,25 @@ } }, "node_modules/testcontainers": { - "version": "10.2.2", + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.9.0.tgz", + "integrity": "sha512-LN+cKAOd61Up9SVMJW+3VFVGeVQG8JBqZhEQo2U0HBfIsAynyAXcsLBSo+KZrOfy9SBz7pGHctWN/KabLDbNFA==", "dev": true, - "license": "MIT", "dependencies": { "@balena/dockerignore": "^1.0.2", + "@types/dockerode": "^3.3.24", "archiver": "^5.3.2", - "async-lock": "^1.4.0", + "async-lock": "^1.4.1", "byline": "^5.0.0", "debug": "^4.3.4", - "docker-compose": "^0.24.2", + "docker-compose": "^0.24.6", "dockerode": "^3.3.5", "get-port": "^5.1.1", "node-fetch": "^2.7.0", "proper-lockfile": "^4.1.2", "properties-reader": "^2.3.0", "ssh-remote-port-forward": "^1.0.4", - "tar-fs": "^3.0.4", + "tar-fs": "^3.0.5", "tmp": "^0.2.1" } }, @@ -8865,8 +8448,9 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -8976,100 +8560,16 @@ "node": ">= 0.6" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/undefsafe": { "version": "2.0.5", "dev": true, "license": "MIT" }, + "node_modules/undici-types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==" + }, "node_modules/unpipe": { "version": "1.0.0", "license": "MIT", @@ -9204,39 +8704,6 @@ "node": ">= 8" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", diff --git a/server/package.json b/server/package.json index 89e50744..9f7189b0 100644 --- a/server/package.json +++ b/server/package.json @@ -1,7 +1,7 @@ { "name": "tdm-calculator-api", - "version": "0.2.50", - "description": "Traffic Data Management Calculator", + "version": "0.2.51", + "description": "Transportation Demand Management Calculator", "repository": { "type": "git", "url": "https://github.com/hackforla/tdm-calculator" @@ -9,7 +9,7 @@ "main": "server.js", "scripts": { "precommit": "lint-staged", - "test": "jest --runInBand --passWithNoTests --forceExit", + "test": "jest --runInBand --passWithNoTests --forceExit --detectOpenHandles", "heroku-postbuild": "cd client && npm i && npm run build", "start": "nodemon", "release-notes": "gren release --override", @@ -30,16 +30,16 @@ "cors": "^2.8.5", "dotenv": "^16.3.1", "error-handler": "^1.0.0", - "express": "^4.18.1", + "express": "^4.19.2", "express-json-validator-middleware": "^3.0.1", "express-pino-logger": "^7.0.0", "jsonwebtoken": "^9.0.2", - "moment": "^2.29.3", - "mssql": "^10.0.1", + "mssql": "^11.0.1", "node-flywaydb": "^3.0.7", "path": "^0.12.7" }, "devDependencies": { + "@testcontainers/mssqlserver": "^10.9.0", "eslint": "^8.53.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-jest": "^27.6.0", diff --git a/server/report.html b/server/report.html index 55e63ac3..2b312255 100644 --- a/server/report.html +++ b/server/report.html @@ -817,7 +817,15 @@ - + +<<<<<<< HEAD + + + +======= + + +>>>>>>> develop
    @@ -8153,6 +8161,1031 @@ See the License for the specific language governing permissions and limitations under the License. +--> +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    No code analysis report available yet

    + +
    +
    +
    +
    +<<<<<<< HEAD +
    +
    + + + + + + +
    +
    Migration report
    Database version: 0
    0 scripts migrated
    Execution Time: 00:00.000s
    +
    +
    Database version: 0
    0 scripts migrated
    Execution Time: 00:00.000s
    +You can read more about the migrate report here +

    No migrations found

    +
    +
    +
    Flyway Exception: Migration V20240203.0923__add_config_table_and_sprocs_1502.sql failed
    +---------------------------------------------------------------------
    +SQL State  : S0001
    +Error Code : 102
    +Message    : Incorrect syntax near '{'.
    +Location   : C:\git\hackforla\tdm\tdm-calculator\server\db\migration\V20240203.0923__add_config_table_and_sprocs_1502.sql (C:\git\hackforla\tdm\tdm-calculator\server\db\migration\V20240203.0923__add_config_table_and_sprocs_1502.sql)
    +Line       : 95
    +Statement  : /* 
    +Populate initially with Okta Development Settings. For other environments, the 
    +Config values will need to be set manually or with another script.
    +*/
    +IF NOT EXISTS (SELECT * FROM Config){
    +  EXEC Config_Insert 'OKTA_ENABLE', 'T'
    +  EXEC Config_Insert 'OKTA_CLIENT_ID', '0oaeecq7dzuXwy5go5d7'
    +  EXEC Config_Insert 'OKTA_ISSUER', 'https://dev-50564150.okta.com/oauth2/default'
    +}
    +
    +
    +
    +
    + +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    Change reporting is not included in your current Flyway license. Upgrade to Flyway Enterprise to gain access.

    + + +
    +
    +
    +
    + +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    Drift reporting is not included in your current Flyway license. Upgrade to Flyway Enterprise to gain access.

    + + +
    +
    +
    +
    + +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    Dry Run is not included in your current Flyway license. Upgrade to Flyway Teams or Enterprise to gain access.

    + + +
    +
    +
    +
    + +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    No code analysis report available yet

    + +
    +
    +
    +
    +
    +
    + + + + + + +
    +
    Migration report
    Database version: 20240203.0923
    1 script migrated
    Execution Time: 00:00.205s
    +
    +
    Database version: 20240203.0923
    1 script migrated
    Execution Time: 00:00.205s
    +You can read more about the migrate report here +
    + + + + + + + + + + + + + + + + + + + + +
    VersionDescriptionCategoryTypeFilepathExecutionTime
    20240203.0923add config table and sprocs 1502VersionedSQLV20240203.0923__add_config_table_and_sprocs_1502.sql00:00.205s
    +
    +
    +
    + +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    Change reporting is not included in your current Flyway license. Upgrade to Flyway Enterprise to gain access.

    + + +
    +
    +
    +
    + +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    Drift reporting is not included in your current Flyway license. Upgrade to Flyway Enterprise to gain access.

    + + +
    +
    +
    +
    + +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    Dry Run is not included in your current Flyway license. Upgrade to Flyway Teams or Enterprise to gain access.

    + + +
    +
    +
    +
    + +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    No code analysis report available yet

    + +
    +
    +
    +
    +
    +
    + + + + + + +
    +
    Migration report
    Database version: 20240203.0923
    17 scripts migrated
    Execution Time: 00:05.780s
    +
    +
    Database version: 20240203.0923
    17 scripts migrated
    Execution Time: 00:05.780s
    +You can read more about the migrate report here +
    + + + + + + + + + + + + + + + + + +<<<<<<< HEAD + +======= + +>>>>>>> develop + + + + + + + +<<<<<<< HEAD + +======= + +>>>>>>> develop + + + + + + + +<<<<<<< HEAD + +======= + +>>>>>>> develop + + + + + + + +<<<<<<< HEAD + +======= + +>>>>>>> develop + + + + + + + +<<<<<<< HEAD + +======= + +>>>>>>> develop + + + + + + + +<<<<<<< HEAD + +======= + +>>>>>>> develop + + + + + + + +<<<<<<< HEAD + +======= + +>>>>>>> develop + + + + + + + +<<<<<<< HEAD + +======= + +>>>>>>> develop + + + + + + + +<<<<<<< HEAD + +======= + +>>>>>>> develop + + + + + + + +<<<<<<< HEAD + +======= + +>>>>>>> develop + + + + + + + +<<<<<<< HEAD + +======= + +>>>>>>> develop + + + + + + + +<<<<<<< HEAD + +======= + +>>>>>>> develop + + + + + + + +<<<<<<< HEAD + +======= + +>>>>>>> develop + + + + + + + +<<<<<<< HEAD + +======= + +>>>>>>> develop + + + + + + + +<<<<<<< HEAD + +======= + +>>>>>>> develop + + + + + + + +<<<<<<< HEAD + + + + + + + + + +======= + +>>>>>>> develop + + +
    VersionDescriptionCategoryTypeFilepathExecutionTime
    20230627.2115create insertAll faqsVersionedSQLV20230627.2115__create_insertAll_faqs.sql00:00.419s00:00.298s
    20230628.0938update insertAllVersionedSQLV20230628.0938__update_insertAll.sql00:00.165s00:00.169s
    20230628.1107update insertAll 3VersionedSQLV20230628.1107__update_insertAll_3.sql00:00.160s00:00.181s
    20230628.1128update category selectAllVersionedSQLV20230628.1128__update_category_selectAll.sql00:00.080s00:00.070s
    20230628.1205update category insertAll 4VersionedSQLV20230628.1205__update_category_insertAll_4.sql00:00.173s00:00.186s
    20230628.1208update category insertAll 5VersionedSQLV20230628.1208__update_category_insertAll_5.sql00:00.164s00:00.199s
    20230628.1230update category insertAll 6VersionedSQLV20230628.1230__update_category_insertAll_6.sql00:00.167s00:00.207s
    20230628.1310update category insertAll 7VersionedSQLV20230628.1310__update_category_insertAll_7.sql00:00.099s00:00.112s
    20230906.2053changes to support hide trash snapshotVersionedSQLV20230906.2053__changes_to_support_hide_trash_snapshot.sql00:00.236s00:00.287s
    20230909.2141archives and deletes users and projectsVersionedSQLV20230909.2141__archives_and_deletes_users_and_projects.sql00:00.278s00:00.370s
    20231003.1432disable strategies when no parking provided 1434VersionedSQLV20231003.1432__disable_strategies_when_no_parking_provided_1434.sql00:00.162s00:00.149s
    20231102.0946modify project snpshot sproc 1437VersionedSQLV20231102.0946__modify_project_snpshot_sproc_1437.sql00:00.071s00:00.075s
    20231120.0924rename publiccomment to feedback 670VersionedSQLV20231120.0924__rename_publiccomment_to_feedback_670.sql00:03.168s00:03.417s
    20231121.1647disable car share parking when no parking provided 1434VersionedSQLV20231121.1647__disable_car_share_parking_when_no_parking_provided_1434.sql00:00.088s00:00.086s
    20240104.1705add-project-rename-sprocVersionedSQLV20240104.1705__add-project-rename-sproc.sql00:00.070s00:00.078s
    20240104.1714add about table to db 1531VersionedSQLV20240104.1714__add_about_table_to_db_1531.sql00:00.074s
    20240203.0923add config table and sprocs 1502VersionedSQLV20240203.0923__add_config_table_and_sprocs_1502.sql00:00.206s00:00.077s
    +
    +
    +<<<<<<< HEAD +
    +======= +
    +>>>>>>> develop + +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    Change reporting is not included in your current Flyway license. Upgrade to Flyway Enterprise to gain access.

    + + +
    +
    +
    +<<<<<<< HEAD +
    +======= +
    +>>>>>>> develop + +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    Drift reporting is not included in your current Flyway license. Upgrade to Flyway Enterprise to gain access.

    + + +
    +
    +
    +<<<<<<< HEAD +
    +======= +
    +>>>>>>> develop + +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    Dry Run is not included in your current Flyway license. Upgrade to Flyway Teams or Enterprise to gain access.

    + + +
    +
    +
    +<<<<<<< HEAD +
    +======= +
    + +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    No code analysis report available yet

    + +
    +
    +
    +
    +
    +
    + + + + + + +
    +
    Migration report
    Database version: 20240204.2230
    1 script migrated
    Execution Time: 00:00.316s
    +
    +
    Database version: 20240204.2230
    1 script migrated
    Execution Time: 00:00.316s
    +You can read more about the migrate report here +
    + + + + + + + + + + + + + + + + + + + + +
    VersionDescriptionCategoryTypeFilepathExecutionTime
    20240204.2230repopulate faqcategory table 1280VersionedSQLV20240204.2230__repopulate_faqcategory_table_1280.sql00:00.316s
    +
    +
    +
    + +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    Change reporting is not included in your current Flyway license. Upgrade to Flyway Enterprise to gain access.

    + + +
    +
    +
    +
    + +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    Drift reporting is not included in your current Flyway license. Upgrade to Flyway Enterprise to gain access.

    + + +
    +
    +
    +
    + +
    +
    +
    + + Explore how to get this report and what it can do for you.
    + +
    + +
    + + + + +

    Dry Run is not included in your current Flyway license. Upgrade to Flyway Teams or Enterprise to gain access.

    + + +
    +
    +
    +
    +>>>>>>> develop +
    diff --git a/server/report.json b/server/report.json index fe03c5a3..56525e26 100644 --- a/server/report.json +++ b/server/report.json @@ -4341,6 +4341,298 @@ "operation": "migrate", "exception": null, "licenseFailed": false + }, + { +<<<<<<< HEAD + "initialSchemaVersion": "20240104.1714", + "targetSchemaVersion": null, + "schemaName": "", + "migrations": [], + "migrationsExecuted": 0, + "success": false, + "flywayVersion": "9.22.3", + "database": "tdmdev", + "warnings": [], + "timestamp": "2024-02-03T09:27:54.207397700", + "operation": "migrate", + "exception": "Migration V20240203.0923__add_config_table_and_sprocs_1502.sql failed\n---------------------------------------------------------------------\nSQL State : S0001\nError Code : 102\nMessage : Incorrect syntax near \u0027{\u0027.\nLocation : C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20240203.0923__add_config_table_and_sprocs_1502.sql (C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20240203.0923__add_config_table_and_sprocs_1502.sql)\nLine : 95\nStatement : /* \nPopulate initially with Okta Development Settings. For other environments, the \nConfig values will need to be set manually or with another script.\n*/\nIF NOT EXISTS (SELECT * FROM Config){\n EXEC Config_Insert \u0027OKTA_ENABLE\u0027, \u0027T\u0027\n EXEC Config_Insert \u0027OKTA_CLIENT_ID\u0027, \u00270oaeecq7dzuXwy5go5d7\u0027\n EXEC Config_Insert \u0027OKTA_ISSUER\u0027, \u0027https://dev-50564150.okta.com/oauth2/default\u0027\n}\n", + "licenseFailed": false + }, + { + "initialSchemaVersion": "20240104.1714", + "targetSchemaVersion": "20240203.0923", + "schemaName": "", + "migrations": [ + { + "category": "Versioned", + "version": "20240203.0923", + "description": "add config table and sprocs 1502", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20240203.0923__add_config_table_and_sprocs_1502.sql", + "executionTime": 205 + } + ], + "migrationsExecuted": 1, + "success": true, + "flywayVersion": "9.22.3", + "database": "tdmdev", + "warnings": [], + "timestamp": "2024-02-03T09:28:55.903701", + "operation": "migrate", + "exception": null, + "licenseFailed": false + }, + { + "initialSchemaVersion": "20230420.1255", + "targetSchemaVersion": "20240203.0923", +======= + "initialSchemaVersion": "20230420.1255", + "targetSchemaVersion": "20240104.1714", +>>>>>>> develop + "schemaName": "", + "migrations": [ + { + "category": "Versioned", + "version": "20230627.2115", + "description": "create insertAll faqs", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20230627.2115__create_insertAll_faqs.sql", +<<<<<<< HEAD + "executionTime": 419 +======= + "executionTime": 298 +>>>>>>> develop + }, + { + "category": "Versioned", + "version": "20230628.0938", + "description": "update insertAll", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20230628.0938__update_insertAll.sql", +<<<<<<< HEAD + "executionTime": 165 +======= + "executionTime": 169 +>>>>>>> develop + }, + { + "category": "Versioned", + "version": "20230628.1107", + "description": "update insertAll 3", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20230628.1107__update_insertAll_3.sql", +<<<<<<< HEAD + "executionTime": 160 +======= + "executionTime": 181 +>>>>>>> develop + }, + { + "category": "Versioned", + "version": "20230628.1128", + "description": "update category selectAll", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20230628.1128__update_category_selectAll.sql", +<<<<<<< HEAD + "executionTime": 80 +======= + "executionTime": 70 +>>>>>>> develop + }, + { + "category": "Versioned", + "version": "20230628.1205", + "description": "update category insertAll 4", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20230628.1205__update_category_insertAll_4.sql", +<<<<<<< HEAD + "executionTime": 173 +======= + "executionTime": 186 +>>>>>>> develop + }, + { + "category": "Versioned", + "version": "20230628.1208", + "description": "update category insertAll 5", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20230628.1208__update_category_insertAll_5.sql", +<<<<<<< HEAD + "executionTime": 164 +======= + "executionTime": 199 +>>>>>>> develop + }, + { + "category": "Versioned", + "version": "20230628.1230", + "description": "update category insertAll 6", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20230628.1230__update_category_insertAll_6.sql", +<<<<<<< HEAD + "executionTime": 167 +======= + "executionTime": 207 +>>>>>>> develop + }, + { + "category": "Versioned", + "version": "20230628.1310", + "description": "update category insertAll 7", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20230628.1310__update_category_insertAll_7.sql", +<<<<<<< HEAD + "executionTime": 99 +======= + "executionTime": 112 +>>>>>>> develop + }, + { + "category": "Versioned", + "version": "20230906.2053", + "description": "changes to support hide trash snapshot", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20230906.2053__changes_to_support_hide_trash_snapshot.sql", +<<<<<<< HEAD + "executionTime": 236 +======= + "executionTime": 287 +>>>>>>> develop + }, + { + "category": "Versioned", + "version": "20230909.2141", + "description": "archives and deletes users and projects", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20230909.2141__archives_and_deletes_users_and_projects.sql", +<<<<<<< HEAD + "executionTime": 278 +======= + "executionTime": 370 +>>>>>>> develop + }, + { + "category": "Versioned", + "version": "20231003.1432", + "description": "disable strategies when no parking provided 1434", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20231003.1432__disable_strategies_when_no_parking_provided_1434.sql", +<<<<<<< HEAD + "executionTime": 162 +======= + "executionTime": 149 +>>>>>>> develop + }, + { + "category": "Versioned", + "version": "20231102.0946", + "description": "modify project snpshot sproc 1437", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20231102.0946__modify_project_snpshot_sproc_1437.sql", +<<<<<<< HEAD + "executionTime": 71 +======= + "executionTime": 75 +>>>>>>> develop + }, + { + "category": "Versioned", + "version": "20231120.0924", + "description": "rename publiccomment to feedback 670", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20231120.0924__rename_publiccomment_to_feedback_670.sql", +<<<<<<< HEAD + "executionTime": 3168 +======= + "executionTime": 3417 +>>>>>>> develop + }, + { + "category": "Versioned", + "version": "20231121.1647", + "description": "disable car share parking when no parking provided 1434", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20231121.1647__disable_car_share_parking_when_no_parking_provided_1434.sql", +<<<<<<< HEAD + "executionTime": 88 +======= + "executionTime": 86 +>>>>>>> develop + }, + { + "category": "Versioned", + "version": "20240104.1705", + "description": "add-project-rename-sproc", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20240104.1705__add-project-rename-sproc.sql", +<<<<<<< HEAD + "executionTime": 70 +======= + "executionTime": 78 +>>>>>>> develop + }, + { + "category": "Versioned", + "version": "20240104.1714", + "description": "add about table to db 1531", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20240104.1714__add_about_table_to_db_1531.sql", +<<<<<<< HEAD + "executionTime": 74 + }, + { + "category": "Versioned", + "version": "20240203.0923", + "description": "add config table and sprocs 1502", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20240203.0923__add_config_table_and_sprocs_1502.sql", + "executionTime": 206 + } + ], + "migrationsExecuted": 17, + "success": true, + "flywayVersion": "9.22.3", + "database": "tdmuat", + "warnings": [], + "timestamp": "2024-02-03T09:36:12.679362400", +======= + "executionTime": 77 + } + ], + "migrationsExecuted": 16, + "success": true, + "flywayVersion": "9.22.3", + "database": "tdmprod", + "warnings": [], + "timestamp": "2024-02-04T21:56:23.415569700", + "operation": "migrate", + "exception": null, + "licenseFailed": false + }, + { + "initialSchemaVersion": "20240104.1714", + "targetSchemaVersion": "20240204.2230", + "schemaName": "", + "migrations": [ + { + "category": "Versioned", + "version": "20240204.2230", + "description": "repopulate faqcategory table 1280", + "type": "SQL", + "filepath": "C:\\git\\hackforla\\tdm\\tdm-calculator\\server\\db\\migration\\V20240204.2230__repopulate_faqcategory_table_1280.sql", + "executionTime": 316 + } + ], + "migrationsExecuted": 1, + "success": true, + "flywayVersion": "9.22.3", + "database": "tdmprod", + "warnings": [], + "timestamp": "2024-02-04T22:33:09.802877700", +>>>>>>> develop + "operation": "migrate", + "exception": null, + "licenseFailed": false } ] } \ No newline at end of file