diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9ac3daab0..4b4a37bc8 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,3 +12,5 @@ updates: - "dependency-bot" commit-message: prefix: "(chore)" + pull-request-branch-name: + separator: "_" diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000..5c5908ae1 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,27 @@ +# https://github.com/marketplace/actions/close-stale-issues + +name: Close inactive issues +on: + schedule: + - cron: "30 1 * * *" + +jobs: + close-issues: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@v5 + with: + days-before-issue-close: 14 + days-before-issue-stale: 30 + days-before-pr-close: 14 + days-before-pr-stale: 30 + close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." + close-pr-message: "This PR was closed because it has been inactive for 14 days since being marked as stale." + stale-issue-label: "stale" + stale-pr-label: "stale" + stale-issue-message: "This issue is stale because it has been open for 30 days with no activity." + stale-pr-message: "This PR is stale because it has been open for 30 days with no activity." + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/locales/data.json b/locales/data.json index 9174a2c62..15fa9371f 100644 --- a/locales/data.json +++ b/locales/data.json @@ -10855,6 +10855,12 @@ "value": "RHEL - Cost Management | OpenShift" } ], + "pageTitleSettings": [ + { + "type": 0, + "value": "Settings - Cost Management | OpenShift" + } + ], "paginationTitle": [ { "options": { @@ -11549,12 +11555,123 @@ "value": "Settings for Cost Management were replaced with new values" } ], + "settingsSuccessTags": [ + { + "options": { + "disable": { + "value": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": " tag disabled" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": " tags disabled" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ] + }, + "enable": { + "value": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": " tag enabled" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": " tags enabled" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ] + }, + "other": { + "value": [] + } + }, + "type": 5, + "value": "value" + } + ], "settingsSuccessTitle": [ { "type": 0, "value": "Application settings saved" } ], + "settingsTagsErrorDesc": [ + { + "type": 0, + "value": "You currently have " + }, + { + "type": 1, + "value": "value" + }, + { + "type": 0, + "value": " tags enabled" + } + ], + "settingsTagsErrorTitle": [ + { + "type": 0, + "value": "You can not enable more than " + }, + { + "type": 1, + "value": "value" + }, + { + "type": 0, + "value": " tags total" + } + ], "settingsTitle": [ { "type": 0, @@ -12103,7 +12220,7 @@ "usageSubtitle": [ { "type": 1, - "value": "count" + "value": "value" }, { "type": 0, @@ -12111,7 +12228,7 @@ }, { "type": 1, - "value": "countUnits" + "value": "units" }, { "type": 0, diff --git a/locales/translations.json b/locales/translations.json index 5bf49d7e5..007b7ab90 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -458,6 +458,7 @@ "pageTitleOptimizations": "Optimizations - Cost Management | OpenShift", "pageTitleOverview": "Overview - Cost Management | OpenShift", "pageTitleRhel": "RHEL - Cost Management | OpenShift", + "pageTitleSettings": "Settings - Cost Management | OpenShift", "paginationTitle": "{placement, select, top {{title} top pagination} bottom {{title} bottom pagination} other {{title} pagination}}", "percent": "{value} %", "percentOfCost": "{value} % of cost", @@ -519,7 +520,10 @@ "settingsSuccessChanges": "Changes will be reflected in report summarizations within 24 hours", "settingsSuccessCostCategoryKeys": "{value, select, enable {{count, plural, one {{count} cost category key enabled} other {{count} cost category keys enabled}}} disable {{count, plural, one {{count} cost category key disabled} other {{count} cost category keys disabled}}} other {}}", "settingsSuccessDesc": "Settings for Cost Management were replaced with new values", + "settingsSuccessTags": "{value, select, enable {{count, plural, one {{count} tag enabled} other {{count} tags enabled}}} disable {{count, plural, one {{count} tag disabled} other {{count} tags disabled}}} other {}}", "settingsSuccessTitle": "Application settings saved", + "settingsTagsErrorDesc": "You currently have {value} tags enabled", + "settingsTagsErrorTitle": "You can not enable more than {value} tags total", "settingsTitle": "Cost Management Settings", "sinceDate": "{dateRange}", "sourceType": "Source type", @@ -552,7 +556,7 @@ "usage": "Usage", "usageCostDesc": "The portion of cost calculated by applying hourly and/or monthly price list rates to metrics.", "usageCostTitle": "Usage cost", - "usageSubtitle": "{count} {countUnits} maximum", + "usageSubtitle": "{value} {units} maximum", "various": "Various", "volumeTitle": "Volume", "workerUnallocated": "Worker unallocated", diff --git a/package-lock.json b/package-lock.json index 641f42bd5..432c36b90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,21 +19,21 @@ "@patternfly/react-tokens": "4.94.6", "@redhat-cloud-services/frontend-components": "^3.11.5", "@redhat-cloud-services/frontend-components-notifications": "^3.2.16", - "@redhat-cloud-services/frontend-components-translations": "^3.2.6", + "@redhat-cloud-services/frontend-components-translations": "^3.2.7", "@redhat-cloud-services/frontend-components-utilities": "^3.7.6", - "@redhat-cloud-services/rbac-client": "^1.2.6", + "@redhat-cloud-services/rbac-client": "^1.2.9", "@unleash/proxy-client-react": "^3.6.0", - "axios": "^1.5.0", + "axios": "^1.5.1", "date-fns": "^2.30.0", "js-file-download": "^0.4.12", "lodash": "^4.17.21", "qs": "^6.11.2", "react": "^18.2.0", - "react-bootstrap": "^2.8.0", + "react-bootstrap": "^2.9.0", "react-dom": "^18.2.0", - "react-intl": "^6.4.4", - "react-redux": "^8.1.2", - "react-router-dom": "^6.15.0", + "react-intl": "^6.4.7", + "react-redux": "^8.1.3", + "react-router-dom": "^6.16.0", "redux": "^4.2.1", "redux-thunk": "^2.4.2", "typesafe-actions": "^5.1.0", @@ -42,44 +42,44 @@ "xstate": "^4.38.2" }, "devDependencies": { - "@formatjs/cli": "^6.1.3", + "@formatjs/cli": "^6.2.0", "@redhat-cloud-services/eslint-config-redhat-cloud-services": "^1.3.0", "@redhat-cloud-services/frontend-components-config": "^5.1.1", "@testing-library/react": "^14.0.0", - "@testing-library/user-event": "^14.4.3", - "@types/jest": "^29.5.4", - "@types/qs": "^6.9.7", - "@types/react": "^18.2.21", - "@types/react-dom": "^18.2.7", - "@types/react-redux": "^7.1.26", + "@testing-library/user-event": "^14.5.1", + "@types/jest": "^29.5.5", + "@types/qs": "^6.9.8", + "@types/react": "^18.2.24", + "@types/react-dom": "^18.2.8", + "@types/react-redux": "^7.1.27", "@types/react-router-dom": "^5.3.3", - "@typescript-eslint/eslint-plugin": "^6.4.1", - "@typescript-eslint/parser": "^6.4.1", + "@typescript-eslint/eslint-plugin": "^6.7.3", + "@typescript-eslint/parser": "^6.7.3", "@xstate/test": "^0.5.1", "aphrodite": "^2.4.0", "copy-webpack-plugin": "^11.0.0", - "eslint": "^8.48.0", - "eslint-plugin-formatjs": "^4.10.3", + "eslint": "^8.50.0", + "eslint-plugin-formatjs": "^4.10.5", "eslint-plugin-jest-dom": "^5.1.0", - "eslint-plugin-jsdoc": "^46.5.0", + "eslint-plugin-jsdoc": "^46.8.2", "eslint-plugin-markdown": "^3.0.1", "eslint-plugin-patternfly-react": "^5.0.0", "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-react": "^7.33.2", "eslint-plugin-simple-import-sort": "^10.0.0", "eslint-plugin-sort-keys-fix": "^1.1.2", - "eslint-plugin-testing-library": "^6.0.1", + "eslint-plugin-testing-library": "^6.0.2", "git-revision-webpack-plugin": "^5.0.0", - "jest": "^29.6.4", - "jest-environment-jsdom": "^29.6.4", - "jest-mock-axios": "^4.7.2", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "jest-mock-axios": "^4.7.3", "jws": "^4.0.0", "npm-run-all": "^4.1.5", - "prettier": "^3.0.2", - "rimraf": "^5.0.1", + "prettier": "^3.0.3", + "rimraf": "^5.0.5", "ts-patch": "^3.0.2", "typescript": "^5.2.2", - "webpack-bundle-analyzer": "^4.9.0" + "webpack-bundle-analyzer": "^4.9.1" }, "engines": { "node": ">=18.15.0", @@ -109,12 +109,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", - "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.10", + "@babel/highlight": "^7.22.13", "chalk": "^2.4.2" }, "engines": { @@ -131,21 +131,21 @@ } }, "node_modules/@babel/core": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.11.tgz", - "integrity": "sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==", + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.17.tgz", + "integrity": "sha512-2EENLmhpwplDux5PSsZnSbnSkB3tZ6QTksgO25xwEL7pIDcNOMhF5v/s6RzwjMZzZzw9Ofc30gHv5ChCC8pifQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-compilation-targets": "^7.22.10", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.11", - "@babel/parser": "^7.22.11", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.22.17", + "@babel/helpers": "^7.22.15", + "@babel/parser": "^7.22.16", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.17", + "@babel/types": "^7.22.17", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -161,9 +161,9 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.22.11.tgz", - "integrity": "sha512-YjOYZ3j7TjV8OhLW6NCtyg8G04uStATEUe5eiLuCZaXz2VSDQ3dsAtm2D+TuQyAqNMUK2WacGo0/uma9Pein1w==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.22.15.tgz", + "integrity": "sha512-yc8OOBIQk1EcRrpizuARSQS0TWAcOMpEJ1aafhNznaeYkeL+OhqnDObGFylB8ka8VFF/sZc+S4RzHyO+3LjQxg==", "dev": true, "dependencies": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", @@ -179,12 +179,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", + "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.10", + "@babel/types": "^7.22.15", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -194,13 +194,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", - "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dev": true, "dependencies": { "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", "browserslist": "^4.21.9", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -244,28 +244,28 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.17.tgz", + "integrity": "sha512-XouDDhQESrLHTpnBtCKExJdyY4gJCdrvH2Pyv8r8kovX2U8G0dRUOT45T9XlbLtuu9CLXP15eusnkprhoPV5iQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-validator-identifier": "^7.22.15" }, "engines": { "node": ">=6.9.0" @@ -317,41 +317,41 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz", + "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.11.tgz", - "integrity": "sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz", + "integrity": "sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", - "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", + "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.5", @@ -363,9 +363,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.11.tgz", - "integrity": "sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g==", + "version": "7.22.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", + "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -552,9 +552,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz", - "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.15.tgz", + "integrity": "sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -563,33 +563,33 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", - "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.17.tgz", + "integrity": "sha512-xK4Uwm0JnAMvxYZxOVecss85WxTEIbTa7bnGyf/+EgCL5Zt3U7htUpEOWv9detPlamGKuRzCqw74xVglDWpPdg==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-function-name": "^7.22.5", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.11", - "@babel/types": "^7.22.11", + "@babel/parser": "^7.22.16", + "@babel/types": "^7.22.17", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -598,13 +598,13 @@ } }, "node_modules/@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz", + "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.15", "to-fast-properties": "^2.0.0" }, "engines": { @@ -814,18 +814,18 @@ } }, "node_modules/@eslint/js": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", - "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", + "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@formatjs/cli": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@formatjs/cli/-/cli-6.1.3.tgz", - "integrity": "sha512-PdTXZTY8LqxwmvFqdifn89gjXnPUpGtGyFs0BnoeLuOuxZFSnBfIs5WQCVMaJnr1+0vNNlXyT0VAIAwjRpf6BA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@formatjs/cli/-/cli-6.2.0.tgz", + "integrity": "sha512-sP04UpocRHYwSovUnunAZHYvCTVbNcaLtWKnr1lETGRUnRRQqnXy/3d2Ce271ELXmNUSde2eHRdu4rv2XaVaiQ==", "dev": true, "bin": { "formatjs": "bin/formatjs" @@ -834,20 +834,20 @@ "node": ">= 16" }, "peerDependencies": { - "@vue/compiler-sfc": "^3.2.34" + "vue": "^3.3.4" }, "peerDependenciesMeta": { - "@vue/compiler-sfc": { + "vue": { "optional": true } } }, "node_modules/@formatjs/ecma402-abstract": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.17.0.tgz", - "integrity": "sha512-6ueQTeJZtwKjmh23bdkq/DMqH4l4bmfvtQH98blOSbiXv/OUiyijSW6jU22IT8BNM1ujCaEvJfTtyCYVH38EMQ==", + "version": "1.17.2", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.17.2.tgz", + "integrity": "sha512-k2mTh0m+IV1HRdU0xXM617tSQTi53tVR2muvYOsBeYcUgEAyxV1FOC7Qj279th3fBVQ+Dj6muvNJZcHSPNdbKg==", "dependencies": { - "@formatjs/intl-localematcher": "0.4.0", + "@formatjs/intl-localematcher": "0.4.2", "tslib": "^2.4.0" } }, @@ -860,35 +860,35 @@ } }, "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.6.0.tgz", - "integrity": "sha512-yT6at0qc0DANw9qM/TU8RZaCtfDXtj4pZM/IC2WnVU80yAcliS3KVDiuUt4jSQAeFL9JS5bc2hARnFmjPdA6qw==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.6.2.tgz", + "integrity": "sha512-nF/Iww7sc5h+1MBCDRm68qpHTCG4xvGzYs/x9HFcDETSGScaJ1Fcadk5U/NXjXeCtzD+DhN4BAwKFVclHfKMdA==", "dependencies": { - "@formatjs/ecma402-abstract": "1.17.0", - "@formatjs/icu-skeleton-parser": "1.6.0", + "@formatjs/ecma402-abstract": "1.17.2", + "@formatjs/icu-skeleton-parser": "1.6.2", "tslib": "^2.4.0" } }, "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.6.0.tgz", - "integrity": "sha512-eMmxNpoX/J1IPUjPGSZwo0Wh+7CEvdEMddP2Jxg1gQJXfGfht/FdW2D5XDFj3VMbOTUQlDIdZJY7uC6O6gjPoA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.6.2.tgz", + "integrity": "sha512-VtB9Slo4ZL6QgtDFJ8Injvscf0xiDd4bIV93SOJTBjUF4xe2nAWOoSjLEtqIG+hlIs1sNrVKAaFo3nuTI4r5ZA==", "dependencies": { - "@formatjs/ecma402-abstract": "1.17.0", + "@formatjs/ecma402-abstract": "1.17.2", "tslib": "^2.4.0" } }, "node_modules/@formatjs/intl": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.9.0.tgz", - "integrity": "sha512-Ym0trUoC/VO6wQu4YHa0H1VR2tEixFRmwZgADkDLm7nD+vv1Ob+/88mUAoT0pwvirFqYKgUKEwp1tFepqyqvVA==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.9.3.tgz", + "integrity": "sha512-hclPdyCF1zk2XmhgdXfl5Sd30QEdRBnIijH7Vc1AWz2K0/saVRrxuL3UYn+m3xEyfOa4yDbTWVbmXDL0XEzlsQ==", "dependencies": { - "@formatjs/ecma402-abstract": "1.17.0", + "@formatjs/ecma402-abstract": "1.17.2", "@formatjs/fast-memoize": "2.2.0", - "@formatjs/icu-messageformat-parser": "2.6.0", - "@formatjs/intl-displaynames": "6.5.0", - "@formatjs/intl-listformat": "7.4.0", - "intl-messageformat": "10.5.0", + "@formatjs/icu-messageformat-parser": "2.6.2", + "@formatjs/intl-displaynames": "6.5.2", + "@formatjs/intl-listformat": "7.4.2", + "intl-messageformat": "10.5.3", "tslib": "^2.4.0" }, "peerDependencies": { @@ -901,40 +901,40 @@ } }, "node_modules/@formatjs/intl-displaynames": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.5.0.tgz", - "integrity": "sha512-sg/nR8ILEdUl+2sWu6jc1nQ5s04yucGlH1RVfatW8TSJ5uG3Yy3vgigi8NNC/BuhcncUNPWqSpTCSI1hA+rhiw==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.5.2.tgz", + "integrity": "sha512-uC2VBlz+WydGTDDpJwMTQuPH3CUpTricr91WH1QMfz5oEHg2sB7mUERcZONE/lu8MOe1jREIx4vBciZEVTqkmA==", "dependencies": { - "@formatjs/ecma402-abstract": "1.17.0", - "@formatjs/intl-localematcher": "0.4.0", + "@formatjs/ecma402-abstract": "1.17.2", + "@formatjs/intl-localematcher": "0.4.2", "tslib": "^2.4.0" } }, "node_modules/@formatjs/intl-listformat": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.4.0.tgz", - "integrity": "sha512-ifupb+balZUAF/Oh3QyGRqPRWGSKwWoMPR0cYZEG7r61SimD+m38oFQqVx/3Fp7LfQFF11m7IS+MlxOo2sKINA==", + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.4.2.tgz", + "integrity": "sha512-+6bSVudEQkf12Hh7kuKt8Xv/MyFlqdwA4V4NLnTZW8uYdF9RxlOELDD0rPaOc2++TMKIzI5o6XXwHPvpL6VrPA==", "dependencies": { - "@formatjs/ecma402-abstract": "1.17.0", - "@formatjs/intl-localematcher": "0.4.0", + "@formatjs/ecma402-abstract": "1.17.2", + "@formatjs/intl-localematcher": "0.4.2", "tslib": "^2.4.0" } }, "node_modules/@formatjs/intl-localematcher": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.4.0.tgz", - "integrity": "sha512-bRTd+rKomvfdS4QDlVJ6TA/Jx1F2h/TBVO5LjvhQ7QPPHp19oPNMIum7W2CMEReq/zPxpmCeB31F9+5gl/qtvw==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.4.2.tgz", + "integrity": "sha512-BGdtJFmaNJy5An/Zan4OId/yR9Ih1OojFjcduX/xOvq798OgWSyDtd6Qd5jqJXwJs1ipe4Fxu9+cshic5Ox2tA==", "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@formatjs/ts-transformer": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@formatjs/ts-transformer/-/ts-transformer-3.13.3.tgz", - "integrity": "sha512-W6+huH4dLYx8eZfZue6fcreNzLZHoPboreqJSkickYCKIOicI35zC0Txb4xCT6kau/DXAKTpNEln3V2NgX6Igg==", + "version": "3.13.5", + "resolved": "https://registry.npmjs.org/@formatjs/ts-transformer/-/ts-transformer-3.13.5.tgz", + "integrity": "sha512-dh2mmZqkId0UeM+FQtmwugpMGvyzTBmXj5LjwD4M5OeSm62tcgkScjqeO/1EetaNS/JkTUBbsFBnHzaDzh3yOw==", "dev": true, "dependencies": { - "@formatjs/icu-messageformat-parser": "2.6.0", + "@formatjs/icu-messageformat-parser": "2.6.2", "@types/json-stable-stringify": "^1.0.32", "@types/node": "14 || 16 || 17", "chalk": "^4.0.0", @@ -1028,9 +1028,9 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -1265,16 +1265,16 @@ } }, "node_modules/@jest/console": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", - "integrity": "sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -1352,15 +1352,15 @@ } }, "node_modules/@jest/core": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz", - "integrity": "sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", - "@jest/reporters": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", @@ -1368,21 +1368,21 @@ "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.6.3", - "jest-config": "^29.6.4", - "jest-haste-map": "^29.6.4", - "jest-message-util": "^29.6.3", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-resolve-dependencies": "^29.6.4", - "jest-runner": "^29.6.4", - "jest-runtime": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", - "jest-watcher": "^29.6.4", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", "micromatch": "^4.0.4", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -1457,9 +1457,9 @@ } }, "node_modules/@jest/core/node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", @@ -1501,37 +1501,37 @@ } }, "node_modules/@jest/environment": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz", - "integrity": "sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, "dependencies": { - "@jest/fake-timers": "^29.6.4", + "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.3" + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz", - "integrity": "sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, "dependencies": { - "expect": "^29.6.4", - "jest-snapshot": "^29.6.4" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz", - "integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, "dependencies": { "jest-get-type": "^29.6.3" @@ -1541,47 +1541,47 @@ } }, "node_modules/@jest/fake-timers": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz", - "integrity": "sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.6.3", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3" + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz", - "integrity": "sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/expect": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", "@jest/types": "^29.6.3", - "jest-mock": "^29.6.3" + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz", - "integrity": "sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", @@ -1595,9 +1595,9 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", - "jest-worker": "^29.6.4", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -1712,12 +1712,12 @@ } }, "node_modules/@jest/test-result": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz", - "integrity": "sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", + "@jest/console": "^29.7.0", "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" @@ -1727,14 +1727,14 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz", - "integrity": "sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.4", + "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -1742,9 +1742,9 @@ } }, "node_modules/@jest/transform": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz", - "integrity": "sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -1755,9 +1755,9 @@ "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -2270,9 +2270,9 @@ } }, "node_modules/@polka/url": { - "version": "1.0.0-next.21", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", - "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", + "version": "1.0.0-next.23", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.23.tgz", + "integrity": "sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==", "dev": true }, "node_modules/@popperjs/core": { @@ -2285,9 +2285,9 @@ } }, "node_modules/@react-aria/ssr": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.7.1.tgz", - "integrity": "sha512-ovVPSD1WlRpZHt7GI9DqJrWG3OIYS+NXQ9y5HIewMJpSe+jPQmMQfyRmgX4EnvmxSlp0u04Wg/7oItcoSIb/RA==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.8.0.tgz", + "integrity": "sha512-Y54xs483rglN5DxbwfCPHxnkvZ+gZ0LbSYmR72LyWPGft8hN/lrl1VRS1EW2SMjnkEWlj+Km2mwvA3kEHDUA0A==", "dependencies": { "@swc/helpers": "^0.5.0" }, @@ -2353,9 +2353,9 @@ } }, "node_modules/@redhat-cloud-services/frontend-components": { - "version": "3.11.5", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components/-/frontend-components-3.11.5.tgz", - "integrity": "sha512-QQz3l1GJ5R1QdcsCabRhf+1EXomW/Nz5UA6dkj4/tYoMaDX1eAFnOzemaLfmymOAF/aMsIDkBOCJlfSrNyxp1A==", + "version": "3.11.8", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components/-/frontend-components-3.11.8.tgz", + "integrity": "sha512-zgBMLMxYOjRH66YGiRGJBhT6WatHiBWMNvZ/cStKdw6WxKBVCF5k4Bk2Bw8fi+xabc/RkR2PetqaUABOvtJuYA==", "dependencies": { "@redhat-cloud-services/frontend-components-utilities": "^3.2.25", "@redhat-cloud-services/types": "^0.0.24", @@ -2619,17 +2619,17 @@ } }, "node_modules/@redhat-cloud-services/frontend-components-translations": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-translations/-/frontend-components-translations-3.2.6.tgz", - "integrity": "sha512-swndL4HO6ZnJ3BylGCyk8BUhtx8U5iic1RiQ1OrrW03TudfJlyR43Q0M2IjUpbs8hPaahBk2qjj8TZUafazUYA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-translations/-/frontend-components-translations-3.2.7.tgz", + "integrity": "sha512-g068CmZ8ff9wGHWyegO/acOVrRd6PGa9KsT1Hb/gl9Ax3LAa/UxlX7Ng7d/HL/KW0VZQB2yavWB8wbS7xsngVQ==", "optionalDependencies": { "@redhat-cloud-services/frontend-components-utilities": "^3.2.25" }, "peerDependencies": { "prop-types": "^15.6.2", - "react": "^16.14.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0", - "react-intl": "^5.17.4" + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-intl": "^6.4.4" } }, "node_modules/@redhat-cloud-services/frontend-components-utilities": { @@ -2666,9 +2666,9 @@ } }, "node_modules/@redhat-cloud-services/rbac-client": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/rbac-client/-/rbac-client-1.2.6.tgz", - "integrity": "sha512-aPmRdZIGPswQIFGIEXwKMw+NC65udBHqfcW1JN4vBglvlUiM35ak9pJQl5AQIHyboH1JaqURbzl/GzJXS952tg==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/rbac-client/-/rbac-client-1.2.9.tgz", + "integrity": "sha512-EyCf8WHaAdCumfK74PBedQvtqWwE42LESWu9KIInyHz6JgzsO0XmM7NsQQeUkO7PJsiHdKY+rpLF/ZXZWAjB4w==", "dependencies": { "axios": "^0.27.2" } @@ -2688,9 +2688,9 @@ "integrity": "sha512-P50stc+mnWLycID46/AKmD/760r5N1eoam//O6MUVriqVorUdht7xkUL78aJZU1vw8WW6xlrDHwz3F6BM148qg==" }, "node_modules/@remix-run/router": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.8.0.tgz", - "integrity": "sha512-mrfKqIHnSZRyIzBcanNJmVQELTnX+qagEDlcKO90RgRBVOZGSGvZKeDihTRfWcqoDn5N/NkUcwWTccnpN18Tfg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.9.0.tgz", + "integrity": "sha512-bV63itrKBC0zdT27qYm6SDZHlkXwFL1xMBuhkn+X7l0+IIhNaH5wuuvZKp6eKhCD4KFhujhfhCT1YxXW6esUIA==", "engines": { "node": ">=14.0.0" } @@ -2735,20 +2735,20 @@ } }, "node_modules/@scalprum/core": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@scalprum/core/-/core-0.5.1.tgz", - "integrity": "sha512-xYj9GRXleYMo2bsbdHN9fJmmwPkUHI9sjEoJU7jWoKzfTZTQWnhHVGjW53XOhtz4NO4lAZGPID7dbuJksNyvuA==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@scalprum/core/-/core-0.5.2.tgz", + "integrity": "sha512-SsDWvrwpUG6U2YsTVAp1gxOgPteTcIBvaru7o7gb+IhbMYB7/UAWi8DuPFA4td+fA38c915grw0SkMsThyf3wQ==", "dependencies": { "@openshift/dynamic-plugin-sdk": "^3.0.0" } }, "node_modules/@scalprum/react-core": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@scalprum/react-core/-/react-core-0.5.1.tgz", - "integrity": "sha512-S3R/cSA9Dlrf4REwDIH80d5M+lEzzYhYHCPOFzmt1Zm3v4sFkFAUx9ZARfyrWK0UOYfXUE/L6D6R1lGgTWm54Q==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@scalprum/react-core/-/react-core-0.5.2.tgz", + "integrity": "sha512-Q5lYICvXB5GfRURJ8fhlEqoMqa7Kv/oyZlF0yYkJYy5yfg6I+AgV2qsBMavAQb/PQMwT3I6wfJ880CjPnbOuRw==", "dependencies": { "@openshift/dynamic-plugin-sdk": "^3.0.0", - "@scalprum/core": "^0.5.1", + "@scalprum/core": "^0.5.2", "lodash": "^4.17.0" }, "peerDependencies": { @@ -2881,9 +2881,9 @@ } }, "node_modules/@swc/helpers": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", - "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", + "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", "dependencies": { "tslib": "^2.4.0" } @@ -2996,9 +2996,9 @@ } }, "node_modules/@testing-library/user-event": { - "version": "14.4.3", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.4.3.tgz", - "integrity": "sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==", + "version": "14.5.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.1.tgz", + "integrity": "sha512-UCcUKrUYGj7ClomOo2SpNVvx4/fkd/2BbIHDCle8A0ax+P3bU7yJwDBDrS6ZwdTMARWTGODX1hEsCcO+7beJjg==", "dev": true, "engines": { "node": ">=12", @@ -3084,18 +3084,18 @@ } }, "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "version": "3.4.36", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", + "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", - "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.1.tgz", + "integrity": "sha512-iaQslNbARe8fctL5Lk+DsmgWOM83lM+7FzP0eQUJs1jd3kBE8NWqBTIT2S8SqQOJjxvt2eyIjpOuYeRXq2AdMw==", "dev": true, "dependencies": { "@types/express-serve-static-core": "*", @@ -3103,9 +3103,9 @@ } }, "node_modules/@types/d3-array": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.6.tgz", - "integrity": "sha512-NHkizg870sKYQn45oZT5ItoHqcgRgJD7KAiWZp4Udc6YdrFH2W0tZ2vv4shRHP+SXHoJ1G8B4I1GWb5oQSGypA==" + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.7.tgz", + "integrity": "sha512-4/Q0FckQ8TBjsB0VdGFemJOG8BLXUB2KKlL0VmZ+eOYeOnTb/wDRQqYWpBmQ6IlvWkXwkYiot+n9Px2aTJ7zGQ==" }, "node_modules/@types/d3-color": { "version": "3.1.0", @@ -3291,9 +3291,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.4", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.4.tgz", - "integrity": "sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A==", + "version": "29.5.5", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.5.tgz", + "integrity": "sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -3362,9 +3362,9 @@ "dev": true }, "node_modules/@types/lodash": { - "version": "4.14.197", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.197.tgz", - "integrity": "sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g==" + "version": "4.14.198", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.198.tgz", + "integrity": "sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg==" }, "node_modules/@types/mdast": { "version": "3.0.12", @@ -3388,9 +3388,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.5.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz", - "integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==", + "version": "20.5.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz", + "integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==", "devOptional": true }, "node_modules/@types/parse-json": { @@ -3411,9 +3411,9 @@ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "version": "6.9.8", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz", + "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==", "dev": true }, "node_modules/@types/range-parser": { @@ -3423,9 +3423,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.2.21", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz", - "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==", + "version": "18.2.24", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.24.tgz", + "integrity": "sha512-Ee0Jt4sbJxMu1iDcetZEIKQr99J1Zfb6D4F3qfUWoR1JpInkY1Wdg4WwCyBjL257D0+jGqSl1twBjV8iCaC0Aw==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -3433,18 +3433,18 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", - "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", + "version": "18.2.8", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.8.tgz", + "integrity": "sha512-bAIvO5lN/U8sPGvs1Xm61rlRHHaq5rp5N3kp9C+NJ/Q41P8iqjkXSu0+/qu8POsjH9pNWb0OYabFez7taP7omw==", "devOptional": true, "dependencies": { "@types/react": "*" } }, "node_modules/@types/react-redux": { - "version": "7.1.26", - "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.26.tgz", - "integrity": "sha512-UKPo7Cm7rswYU6PH6CmTNCRv5NYF3HrgKuHEYTK8g/3czYLrUux50gQ2pkxc9c7ZpQZi+PNhgmI8oNIRoiVIxg==", + "version": "7.1.27", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.27.tgz", + "integrity": "sha512-xj7d9z32p1K/eBmO+OEy+qfaWXtcPlN8f1Xk3Ne0p/ZRQ867RI5bQ/bpBtxbqU1AHNhKJSgGvld/P2myU2uYkg==", "dev": true, "dependencies": { "@types/hoist-non-react-statics": "^3.3.0", @@ -3494,9 +3494,9 @@ "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==", "dev": true }, "node_modules/@types/send": { @@ -3575,9 +3575,9 @@ "dev": true }, "node_modules/@types/uglify-js": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.1.tgz", - "integrity": "sha512-GkewRA4i5oXacU/n4MA9+bLgt5/L3F1mKrYvFGm7r2ouLXhRKjuWwo9XHNnbx6WF3vlGW21S3fCvgqxvxXXc5g==", + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.2.tgz", + "integrity": "sha512-9SjrHO54LINgC/6Ehr81NjAxAYvwEZqjUHLjJYvC4Nmr9jbLQCIZbWSvl4vXQkkmR1UAuaKDycau3O1kWGFyXQ==", "dev": true, "dependencies": { "source-map": "^0.6.1" @@ -3593,9 +3593,9 @@ } }, "node_modules/@types/unist": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.7.tgz", - "integrity": "sha512-cputDpIbFgLUaGQn6Vqg3/YsJwxUwHLO13v3i5ouxT4lat0khip9AEWxtERujXV9wxIB1EyF97BSJFt6vpdI8g==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.8.tgz", + "integrity": "sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw==", "dev": true }, "node_modules/@types/use-sync-external-store": { @@ -3677,16 +3677,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.4.1.tgz", - "integrity": "sha512-3F5PtBzUW0dYlq77Lcqo13fv+58KDwUib3BddilE8ajPJT+faGgxmI9Sw+I8ZS22BYwoir9ZhNXcLi+S+I2bkw==", + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.3.tgz", + "integrity": "sha512-vntq452UHNltxsaaN+L9WyuMch8bMd9CqJ3zhzTPXXidwbf5mqqKCVXEuvRZUqLJSTLeWE65lQwyXsRGnXkCTA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.4.1", - "@typescript-eslint/type-utils": "6.4.1", - "@typescript-eslint/utils": "6.4.1", - "@typescript-eslint/visitor-keys": "6.4.1", + "@typescript-eslint/scope-manager": "6.7.3", + "@typescript-eslint/type-utils": "6.7.3", + "@typescript-eslint/utils": "6.7.3", + "@typescript-eslint/visitor-keys": "6.7.3", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -3745,15 +3745,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.4.1.tgz", - "integrity": "sha512-610G6KHymg9V7EqOaNBMtD1GgpAmGROsmfHJPXNLCU9bfIuLrkdOygltK784F6Crboyd5tBFayPB7Sf0McrQwg==", + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.3.tgz", + "integrity": "sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.4.1", - "@typescript-eslint/types": "6.4.1", - "@typescript-eslint/typescript-estree": "6.4.1", - "@typescript-eslint/visitor-keys": "6.4.1", + "@typescript-eslint/scope-manager": "6.7.3", + "@typescript-eslint/types": "6.7.3", + "@typescript-eslint/typescript-estree": "6.7.3", + "@typescript-eslint/visitor-keys": "6.7.3", "debug": "^4.3.4" }, "engines": { @@ -3773,13 +3773,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.1.tgz", - "integrity": "sha512-p/OavqOQfm4/Hdrr7kvacOSFjwQ2rrDVJRPxt/o0TOWdFnjJptnjnZ+sYDR7fi4OimvIuKp+2LCkc+rt9fIW+A==", + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.3.tgz", + "integrity": "sha512-wOlo0QnEou9cHO2TdkJmzF7DFGvAKEnB82PuPNHpT8ZKKaZu6Bm63ugOTn9fXNJtvuDPanBc78lGUGGytJoVzQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.1", - "@typescript-eslint/visitor-keys": "6.4.1" + "@typescript-eslint/types": "6.7.3", + "@typescript-eslint/visitor-keys": "6.7.3" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -3790,13 +3790,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.4.1.tgz", - "integrity": "sha512-7ON8M8NXh73SGZ5XvIqWHjgX2f+vvaOarNliGhjrJnv1vdjG0LVIz+ToYfPirOoBi56jxAKLfsLm40+RvxVVXA==", + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.3.tgz", + "integrity": "sha512-Fc68K0aTDrKIBvLnKTZ5Pf3MXK495YErrbHb1R6aTpfK5OdSFj0rVN7ib6Tx6ePrZ2gsjLqr0s98NG7l96KSQw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.4.1", - "@typescript-eslint/utils": "6.4.1", + "@typescript-eslint/typescript-estree": "6.7.3", + "@typescript-eslint/utils": "6.7.3", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -3817,9 +3817,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.1.tgz", - "integrity": "sha512-zAAopbNuYu++ijY1GV2ylCsQsi3B8QvfPHVqhGdDcbx/NK5lkqMnCGU53amAjccSpk+LfeONxwzUhDzArSfZJg==", + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.3.tgz", + "integrity": "sha512-4g+de6roB2NFcfkZb439tigpAMnvEIg3rIjWQ+EM7IBaYt/CdJt6em9BJ4h4UpdgaBWdmx2iWsafHTrqmgIPNw==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -3830,13 +3830,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.1.tgz", - "integrity": "sha512-xF6Y7SatVE/OyV93h1xGgfOkHr2iXuo8ip0gbfzaKeGGuKiAnzS+HtVhSPx8Www243bwlW8IF7X0/B62SzFftg==", + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.3.tgz", + "integrity": "sha512-YLQ3tJoS4VxLFYHTw21oe1/vIZPRqAO91z6Uv0Ss2BKm/Ag7/RVQBcXTGcXhgJMdA4U+HrKuY5gWlJlvoaKZ5g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.1", - "@typescript-eslint/visitor-keys": "6.4.1", + "@typescript-eslint/types": "6.7.3", + "@typescript-eslint/visitor-keys": "6.7.3", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -3890,17 +3890,17 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.4.1.tgz", - "integrity": "sha512-F/6r2RieNeorU0zhqZNv89s9bDZSovv3bZQpUNOmmQK1L80/cV4KEu95YUJWi75u5PhboFoKUJBnZ4FQcoqhDw==", + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.3.tgz", + "integrity": "sha512-vzLkVder21GpWRrmSR9JxGZ5+ibIUSudXlW52qeKpzUEQhRSmyZiVDDj3crAth7+5tmN1ulvgKaCU2f/bPRCzg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.4.1", - "@typescript-eslint/types": "6.4.1", - "@typescript-eslint/typescript-estree": "6.4.1", + "@typescript-eslint/scope-manager": "6.7.3", + "@typescript-eslint/types": "6.7.3", + "@typescript-eslint/typescript-estree": "6.7.3", "semver": "^7.5.4" }, "engines": { @@ -3948,12 +3948,12 @@ "dev": true }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.1.tgz", - "integrity": "sha512-y/TyRJsbZPkJIZQXrHfdnxVnxyKegnpEvnRGNam7s3TRR2ykGefEWOhaef00/UUN3IZxizS7BTO3svd3lCOJRQ==", + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.3.tgz", + "integrity": "sha512-HEVXkU9IB+nk9o63CeICMHxFWbHWr3E1mpilIQBe9+7L/lH97rleFLVtYsfnWB+JVMaiFnEaxvknvmIzX+CqVg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.4.1", + "@typescript-eslint/types": "6.7.3", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -4508,15 +4508,15 @@ "dev": true }, "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "is-string": "^1.0.7" }, "engines": { @@ -4545,16 +4545,16 @@ } }, "node_modules/array.prototype.findlastindex": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.2.tgz", - "integrity": "sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", + "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" + "get-intrinsic": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -4564,14 +4564,14 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -4582,14 +4582,14 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -4600,27 +4600,28 @@ } }, "node_modules/array.prototype.tosorted": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", - "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz", + "integrity": "sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" + "get-intrinsic": "^1.2.1" } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", - "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", "dev": true, "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" @@ -4648,15 +4649,16 @@ } }, "node_modules/assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "dev": true, "dependencies": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" } }, "node_modules/assert-plus": { @@ -4787,18 +4789,18 @@ "peer": true }, "node_modules/axe-core": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.2.tgz", - "integrity": "sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.8.0.tgz", + "integrity": "sha512-ZtlVZobOeDQhb/y2lMK6mznDw7TJHDNcKx5/bbBkFvArIQ5CVFhSI6hWWQnMx9I8cNmNmZ30wpDyOC2E2nvgbQ==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/axios": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", - "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", + "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -4815,12 +4817,12 @@ } }, "node_modules/babel-jest": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", - "integrity": "sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, "dependencies": { - "@jest/transform": "^29.6.4", + "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", "babel-preset-jest": "^29.6.3", @@ -5488,9 +5490,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001524", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz", - "integrity": "sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA==", + "version": "1.0.30001529", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001529.tgz", + "integrity": "sha512-n2pUQYGAkrLG4QYj2desAh+NqsJpHbNmVZz87imptDdxLAtjxary7Df/psdfyDGmskJK/9Dt9cPnx5RZ3CU4Og==", "dev": true, "funding": [ { @@ -6294,9 +6296,9 @@ "hasInstallScript": true }, "node_modules/core-js-pure": { - "version": "3.32.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.32.1.tgz", - "integrity": "sha512-f52QZwkFVDPf7UEQZGHKx6NYxsxmVGJe5DIvbzOdRMJlmT6yv0KDjR8rmy3ngr/t5wU54c7Sp/qIJH0ppbhVpQ==", + "version": "3.32.2", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.32.2.tgz", + "integrity": "sha512-Y2rxThOuNywTjnX/PgA5vWM6CZ9QB9sz9oGeCixV8MqXZO70z/5SHzf9EeBrEBK0PN36DnEBBu9O/aGWzKuMZQ==", "dev": true, "hasInstallScript": true, "funding": { @@ -6334,6 +6336,97 @@ "node": ">=10" } }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/create-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/create-jest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/create-jest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/create-jest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/create-jest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -6546,9 +6639,9 @@ } }, "node_modules/cypress/node_modules/@types/node": { - "version": "16.18.46", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.46.tgz", - "integrity": "sha512-Mnq3O9Xz52exs3mlxMcQuA7/9VFe/dXcrgAyfjLkABIqxXKOgBRjyazTxUbjsxDa4BP7hhPliyjVTP9RDP14xg==", + "version": "16.18.48", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.48.tgz", + "integrity": "sha512-mlaecDKQ7rIZrYD7iiKNdzFb6e/qD5I9U1rAhq+Fd+DWvYVs+G2kv74UFHmSOlg5+i/vF3XxuR522V4u8BqO+Q==", "peer": true }, "node_modules/cypress/node_modules/ansi-styles": { @@ -7535,9 +7628,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.503", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.503.tgz", - "integrity": "sha512-LF2IQit4B0VrUHFeQkWhZm97KuJSGF2WJqq1InpY+ECpFRkXd8yTIaTtJxsO0OKDmiBYwWqcrNaXOurn2T2wiA==", + "version": "1.4.512", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.512.tgz", + "integrity": "sha512-1W8wRbYlQE4ph7eoj3TJ+uqwO6+xvAE/L+KGU7WTQQvX3tnSIGZAb90MTsMoJqzntamiwJhBAj4WZmygXhsOUg==", "dev": true }, "node_modules/emittery": { @@ -7815,12 +7908,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", - "dev": true - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -7876,16 +7963,16 @@ } }, "node_modules/eslint": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", - "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz", + "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.48.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint/js": "8.50.0", + "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.12.4", @@ -8145,20 +8232,20 @@ } }, "node_modules/eslint-plugin-formatjs": { - "version": "4.10.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-formatjs/-/eslint-plugin-formatjs-4.10.3.tgz", - "integrity": "sha512-EHKuEMCmWhAiMdCc8oZU8qBAvnvHPUiJuhGxPqA+GX2Nb7GBsGm2o616KYnSSffDisK+v0E9TDCrS8oJ0QLgcw==", + "version": "4.10.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-formatjs/-/eslint-plugin-formatjs-4.10.5.tgz", + "integrity": "sha512-pBPA4idiYHXPQMrIb9/Le+D0snlNa7MFQsw12yIzyva/z9uz0u/4NOK3NkfyENMBNMeTX2tZXtugk9FyqM5SRw==", "dev": true, "dependencies": { - "@formatjs/icu-messageformat-parser": "2.6.0", - "@formatjs/ts-transformer": "3.13.3", + "@formatjs/icu-messageformat-parser": "2.6.2", + "@formatjs/ts-transformer": "3.13.5", "@types/eslint": "7 || 8", "@types/picomatch": "^2.3.0", - "@typescript-eslint/typescript-estree": "5.59.0", + "@typescript-eslint/typescript-estree": "5.62.0", "emoji-regex": "^10.2.1", "magic-string": "^0.30.0", "picomatch": "^2.3.1", - "tslib": "2.5.0", + "tslib": "2.6.0", "typescript": "^4.7 || 5", "unicode-emoji-utils": "^1.1.1" }, @@ -8167,9 +8254,9 @@ } }, "node_modules/eslint-plugin-formatjs/node_modules/@typescript-eslint/types": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.0.tgz", - "integrity": "sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -8180,13 +8267,13 @@ } }, "node_modules/eslint-plugin-formatjs/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.0.tgz", - "integrity": "sha512-sUNnktjmI8DyGzPdZ8dRwW741zopGxltGs/SAPgGL/AAgDpiLsCFLcMNSpbfXfmnNeHmK9h3wGmCkGRGAoUZAg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.0", - "@typescript-eslint/visitor-keys": "5.59.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -8207,12 +8294,12 @@ } }, "node_modules/eslint-plugin-formatjs/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.0.tgz", - "integrity": "sha512-qZ3iXxQhanchCeaExlKPV3gDQFxMUmU35xfd5eCXB6+kUw1TUAbIy2n7QIrwz9s98DQLzNWyHp61fY0da4ZcbA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.0", + "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -8263,9 +8350,9 @@ } }, "node_modules/eslint-plugin-formatjs/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", + "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==", "dev": true }, "node_modules/eslint-plugin-formatjs/node_modules/yallist": { @@ -8521,9 +8608,9 @@ "dev": true }, "node_modules/eslint-plugin-jsdoc": { - "version": "46.5.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.5.0.tgz", - "integrity": "sha512-aulXdA4I1dyWpzyS1Nh/GNoS6PavzeucxEapnMR4JUERowWvaEk2Y4A5irpHAcdXtBBHLVe8WIhdXNjoAlGQgA==", + "version": "46.8.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.8.2.tgz", + "integrity": "sha512-5TSnD018f3tUJNne4s4gDWQflbsgOycIKEUBoCLn6XtBMgNHxQFmV8vVxUtiPxAQq8lrX85OaSG/2gnctxw9uQ==", "dev": true, "dependencies": { "@es-joy/jsdoccomment": "~0.40.1", @@ -8973,9 +9060,9 @@ } }, "node_modules/eslint-plugin-testing-library": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-6.0.1.tgz", - "integrity": "sha512-CEYtjpcF3hAaQtYsTZqciR7s5z+T0LCMTwJeW+pz6kBnGtc866wAKmhaiK2Gsjc2jWNP7Gt6zhNr2DE1ZW4e+g==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-6.0.2.tgz", + "integrity": "sha512-3BV6FWtLbpKFb4Y1obSdt8PC9xSqz6T+7EHB/6pSCXqVjKPoS67ck903feKMKQphd5VhrX+N51yHuVaPa7elsw==", "dev": true, "dependencies": { "@typescript-eslint/utils": "^5.58.0" @@ -9493,16 +9580,16 @@ } }, "node_modules/expect": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", - "integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.6.4", + "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3" + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -10192,15 +10279,15 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "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==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" @@ -11251,13 +11338,13 @@ } }, "node_modules/intl-messageformat": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.0.tgz", - "integrity": "sha512-AvojYuOaRb6r2veOKfTVpxH9TrmjSdc5iR9R5RgBwrDZYSmAAFVT+QLbW3C4V7Qsg0OguMp67Q/EoUkxZzXRGw==", + "version": "10.5.3", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.3.tgz", + "integrity": "sha512-TzKn1uhJBMyuKTO4zUX47SU+d66fu1W9tVzIiZrQ6hBqQQeYscBMIzKL/qEXnFbJrH9uU5VV3+T5fWib4SIcKA==", "dependencies": { - "@formatjs/ecma402-abstract": "1.17.0", + "@formatjs/ecma402-abstract": "1.17.2", "@formatjs/fast-memoize": "2.2.0", - "@formatjs/icu-messageformat-parser": "2.6.0", + "@formatjs/icu-messageformat-parser": "2.6.2", "tslib": "^2.4.0" } }, @@ -12105,22 +12192,21 @@ } }, "node_modules/iterator.prototype": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.0.tgz", - "integrity": "sha512-rjuhAk1AJ1fssphHD0IFV6TWL40CwRZ53FrztKx43yk2v6rguBYsY4Bj1VU4HmoMmKwZUlx7mfnhDf9cOp4YTw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.1.tgz", + "integrity": "sha512-9E+nePc8C9cnQldmNl6bgpTY6zI4OPRZd97fhJ/iVZ1GifIUDVV5F6x1nEDqpe8KaMEZGT4xgrwKQDxXnjOIZQ==", "dev": true, "dependencies": { - "define-properties": "^1.1.4", - "get-intrinsic": "^1.1.3", + "define-properties": "^1.2.0", + "get-intrinsic": "^1.2.1", "has-symbols": "^1.0.3", - "has-tostringtag": "^1.0.0", "reflect.getprototypeof": "^1.0.3" } }, "node_modules/jackspeak": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.0.tgz", - "integrity": "sha512-uKmsITSsF4rUWQHzqaRUuyAir3fZfW3f202Ee34lz/gZCi970CPZwyQXLGNgWJvvZbvFyzeyGq0+4fcG/mBKZg==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -12136,15 +12222,15 @@ } }, "node_modules/jest": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", - "integrity": "sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "dependencies": { - "@jest/core": "^29.6.4", + "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.6.4" + "jest-cli": "^29.7.0" }, "bin": { "jest": "bin/jest.js" @@ -12162,13 +12248,13 @@ } }, "node_modules/jest-changed-files": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", - "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, "dependencies": { "execa": "^5.0.0", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "p-limit": "^3.1.0" }, "engines": { @@ -12220,28 +12306,28 @@ } }, "node_modules/jest-circus": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz", - "integrity": "sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/expect": "^29.6.4", - "@jest/test-result": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-runtime": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -12309,9 +12395,9 @@ } }, "node_modules/jest-circus/node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", @@ -12353,22 +12439,21 @@ } }, "node_modules/jest-cli": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz", - "integrity": "sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, "dependencies": { - "@jest/core": "^29.6.4", - "@jest/test-result": "^29.6.4", + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "chalk": "^4.0.0", + "create-jest": "^29.7.0", "exit": "^0.1.2", - "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", - "prompts": "^2.0.1", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "yargs": "^17.3.1" }, "bin": { @@ -12457,31 +12542,31 @@ } }, "node_modules/jest-config": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz", - "integrity": "sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.4", + "@jest/test-sequencer": "^29.7.0", "@jest/types": "^29.6.3", - "babel-jest": "^29.6.4", + "babel-jest": "^29.7.0", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.4", - "jest-environment-node": "^29.6.4", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", "jest-get-type": "^29.6.3", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-runner": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -12560,9 +12645,9 @@ } }, "node_modules/jest-config/node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", @@ -12604,15 +12689,15 @@ } }, "node_modules/jest-diff": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz", - "integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -12677,9 +12762,9 @@ } }, "node_modules/jest-diff/node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", @@ -12721,9 +12806,9 @@ } }, "node_modules/jest-docblock": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", - "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "dependencies": { "detect-newline": "^3.0.0" @@ -12733,16 +12818,16 @@ } }, "node_modules/jest-each": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", - "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", "jest-get-type": "^29.6.3", - "jest-util": "^29.6.3", - "pretty-format": "^29.6.3" + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -12807,9 +12892,9 @@ } }, "node_modules/jest-each/node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", @@ -12851,18 +12936,18 @@ } }, "node_modules/jest-environment-jsdom": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.6.4.tgz", - "integrity": "sha512-K6wfgUJ16DoMs02JYFid9lOsqfpoVtyJxpRlnTxUHzvZWBnnh2VNGRB9EC1Cro96TQdq5TtSjb3qUjNaJP9IyA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/jsdom": "^20.0.0", "@types/node": "*", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", "jsdom": "^20.0.0" }, "engines": { @@ -12878,17 +12963,17 @@ } }, "node_modules/jest-environment-node": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz", - "integrity": "sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3" + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -12904,9 +12989,9 @@ } }, "node_modules/jest-haste-map": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz", - "integrity": "sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", @@ -12916,8 +13001,8 @@ "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", - "jest-util": "^29.6.3", - "jest-worker": "^29.6.4", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -12929,13 +13014,13 @@ } }, "node_modules/jest-leak-detector": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", - "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, "dependencies": { "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -12954,9 +13039,9 @@ } }, "node_modules/jest-leak-detector/node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", @@ -12974,15 +13059,15 @@ "dev": true }, "node_modules/jest-matcher-utils": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz", - "integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.6.4", + "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -13047,9 +13132,9 @@ } }, "node_modules/jest-matcher-utils/node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", @@ -13091,9 +13176,9 @@ } }, "node_modules/jest-message-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", - "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", @@ -13102,7 +13187,7 @@ "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -13169,9 +13254,9 @@ } }, "node_modules/jest-message-util/node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", @@ -13213,56 +13298,30 @@ } }, "node_modules/jest-mock": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", - "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.6.3" + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-mock-axios": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/jest-mock-axios/-/jest-mock-axios-4.7.2.tgz", - "integrity": "sha512-8xrkCuEsscOLSw4Noi40Pq5tQMlKg3tbDZ0XNkf3Mv0Yq+z6+qS2tToimNs/3RIsEzoPXHTf2+mcySaNaYXIyw==", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/jest-mock-axios/-/jest-mock-axios-4.7.3.tgz", + "integrity": "sha512-RHHdCZWreeX1EAl77u46yqYJG5aKX9l4zsCwf6wsIb3uy3w/XaEC5n4wbyluNujXQSZfNH1ir8OXinoewYQkUw==", "dev": true, "dependencies": { - "@jest/globals": "^29.5.0", - "jest": "~29.5.0", + "@jest/globals": "^29.7.0", + "jest": "~29.7.0", "synchronous-promise": "^2.0.17" } }, - "node_modules/jest-mock-axios/node_modules/jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", - "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", - "dev": true, - "dependencies": { - "@jest/core": "^29.5.0", - "@jest/types": "^29.5.0", - "import-local": "^3.0.2", - "jest-cli": "^29.5.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, "node_modules/jest-pnp-resolver": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", @@ -13290,17 +13349,17 @@ } }, "node_modules/jest-resolve": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz", - "integrity": "sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.7.0", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -13310,13 +13369,13 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz", - "integrity": "sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, "dependencies": { "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.6.4" + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -13393,30 +13452,30 @@ } }, "node_modules/jest-runner": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz", - "integrity": "sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", - "@jest/environment": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.6.3", - "jest-environment-node": "^29.6.4", - "jest-haste-map": "^29.6.4", - "jest-leak-detector": "^29.6.3", - "jest-message-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-runtime": "^29.6.4", - "jest-util": "^29.6.3", - "jest-watcher": "^29.6.4", - "jest-worker": "^29.6.4", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -13495,17 +13554,17 @@ } }, "node_modules/jest-runtime": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz", - "integrity": "sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", - "@jest/globals": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", @@ -13513,13 +13572,13 @@ "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-mock": "^29.6.3", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -13598,9 +13657,9 @@ } }, "node_modules/jest-snapshot": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz", - "integrity": "sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -13608,20 +13667,20 @@ "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.6.4", + "expect": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.4", + "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "natural-compare": "^1.4.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "semver": "^7.5.3" }, "engines": { @@ -13699,9 +13758,9 @@ } }, "node_modules/jest-snapshot/node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", @@ -13764,9 +13823,9 @@ "dev": true }, "node_modules/jest-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", - "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", @@ -13851,9 +13910,9 @@ } }, "node_modules/jest-validate": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", - "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", @@ -13861,7 +13920,7 @@ "chalk": "^4.0.0", "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -13938,9 +13997,9 @@ } }, "node_modules/jest-validate/node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", @@ -13982,18 +14041,18 @@ } }, "node_modules/jest-watcher": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz", - "integrity": "sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.4", + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "string-length": "^4.0.1" }, "engines": { @@ -14071,13 +14130,13 @@ } }, "node_modules/jest-worker": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", - "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -14623,6 +14682,30 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.escape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", + "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==", + "dev": true + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true + }, + "node_modules/lodash.invokemap": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.invokemap/-/lodash.invokemap-4.6.0.tgz", + "integrity": "sha512-CfkycNtMqgUlfjfdh2BhKO/ZXrP8ePOX5lEU/g0R3ItJcnuxWDwokMGKx1hWcfOikmyOVx6X9IwWnDGlgKl61w==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -14635,6 +14718,18 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "peer": true }, + "node_modules/lodash.pullall": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.pullall/-/lodash.pullall-4.2.0.tgz", + "integrity": "sha512-VhqxBKH0ZxPpLhiu68YD1KnHmbhQJQctcipvmFnqIBDYzcIHzf3Zpu0tpeOKtR4x76p9yohc506eGdOjTmyIBg==", + "dev": true + }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "dev": true + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -15161,9 +15256,9 @@ } }, "node_modules/minipass": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", - "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -15566,14 +15661,14 @@ } }, "node_modules/object.fromentries": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", - "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -15583,25 +15678,25 @@ } }, "node_modules/object.groupby": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.0.tgz", - "integrity": "sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", + "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", - "es-abstract": "^1.21.2", + "es-abstract": "^1.22.1", "get-intrinsic": "^1.2.1" } }, "node_modules/object.hasown": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", - "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", + "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", "dev": true, "dependencies": { - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -16268,9 +16363,9 @@ } }, "node_modules/postcss": { - "version": "8.4.28", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz", - "integrity": "sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==", + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", "funding": [ { "type": "opencollective", @@ -16382,9 +16477,9 @@ } }, "node_modules/prettier": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.2.tgz", - "integrity": "sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -16564,9 +16659,9 @@ } }, "node_modules/pure-rand": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", - "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.3.tgz", + "integrity": "sha512-KddyFewCsO0j3+np81IQ+SweXLDnDQTs5s67BOnrYmYe/yNmUhttQyGsYzy8yUnoljGAQ9sl38YB4vH8ur7Y+w==", "dev": true, "funding": [ { @@ -16663,14 +16758,14 @@ } }, "node_modules/react-bootstrap": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.8.0.tgz", - "integrity": "sha512-e/aNtxl0Z2ozrIaR82jr6Zz7ss9GSoaXpQaxmvtDUsTZIq/XalkduR/ZXP6vbQHz2T4syvjA+4FbtwELxxmpww==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.9.0.tgz", + "integrity": "sha512-dGh6fGjqR9MBzPOp2KbXJznt1Zy6SWepXYUdxMT18Zu/wJ73HCU8JNZe9dfzjmVssZYsJH9N3HHE4wAtQvNz7g==", "dependencies": { - "@babel/runtime": "^7.21.0", + "@babel/runtime": "^7.22.5", "@restart/hooks": "^0.4.9", - "@restart/ui": "^1.6.3", - "@types/react-transition-group": "^4.4.5", + "@restart/ui": "^1.6.6", + "@types/react-transition-group": "^4.4.6", "classnames": "^2.3.2", "dom-helpers": "^5.2.1", "invariant": "^2.2.4", @@ -16737,19 +16832,19 @@ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" }, "node_modules/react-intl": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.4.4.tgz", - "integrity": "sha512-/C9Sl/5//ohfkNG6AWlJuf4BhTXsbzyk93K62A4zRhSPANyOGpKZ+fWhN+TLfFd5YjDUHy+exU/09y0w1bO4Xw==", - "dependencies": { - "@formatjs/ecma402-abstract": "1.17.0", - "@formatjs/icu-messageformat-parser": "2.6.0", - "@formatjs/intl": "2.9.0", - "@formatjs/intl-displaynames": "6.5.0", - "@formatjs/intl-listformat": "7.4.0", + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.4.7.tgz", + "integrity": "sha512-0hnOHAZhxTFqD1hGTxrF40qNyZJPPYiGhWIIxIz0Udz+3e3c7sdN80qlxArR+AbJ+jb5ALXZkJYH20+GPFCM0Q==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.17.2", + "@formatjs/icu-messageformat-parser": "2.6.2", + "@formatjs/intl": "2.9.3", + "@formatjs/intl-displaynames": "6.5.2", + "@formatjs/intl-listformat": "7.4.2", "@types/hoist-non-react-statics": "^3.3.1", "@types/react": "16 || 17 || 18", "hoist-non-react-statics": "^3.3.2", - "intl-messageformat": "10.5.0", + "intl-messageformat": "10.5.3", "tslib": "^2.4.0" }, "peerDependencies": { @@ -16773,9 +16868,9 @@ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, "node_modules/react-redux": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.2.tgz", - "integrity": "sha512-xJKYI189VwfsFc4CJvHqHlDrzyFTY/3vZACbE+rr/zQ34Xx1wQfB4OTOSeOSNrF6BDVe8OOdxIrAnMGXA3ggfw==", + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", + "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", "dependencies": { "@babel/runtime": "^7.12.1", "@types/hoist-non-react-statics": "^3.3.1", @@ -16825,11 +16920,11 @@ } }, "node_modules/react-router": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.15.0.tgz", - "integrity": "sha512-NIytlzvzLwJkCQj2HLefmeakxxWHWAP+02EGqWEZy+DgfHHKQMUoBBjUQLOtFInBMhWtb3hiUy6MfFgwLjXhqg==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.16.0.tgz", + "integrity": "sha512-VT4Mmc4jj5YyjpOi5jOf0I+TYzGpvzERy4ckNSvSh2RArv8LLoCxlsZ2D+tc7zgjxcY34oTz2hZaeX5RVprKqA==", "dependencies": { - "@remix-run/router": "1.8.0" + "@remix-run/router": "1.9.0" }, "engines": { "node": ">=14.0.0" @@ -16839,12 +16934,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.15.0.tgz", - "integrity": "sha512-aR42t0fs7brintwBGAv2+mGlCtgtFQeOzK0BM1/OiqEzRejOZtpMZepvgkscpMUnKb8YO84G7s3LsHnnDNonbQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.16.0.tgz", + "integrity": "sha512-aTfBLv3mk/gaKLxgRDUPbPw+s4Y/O+ma3rEN1u8EgEpLpPe6gNjIsWt9rxushMHHMb7mSwxRGdGlGdvmFsyPIg==", "dependencies": { - "@remix-run/router": "1.8.0", - "react-router": "6.15.0" + "@remix-run/router": "1.9.0", + "react-router": "6.16.0" }, "engines": { "node": ">=14.0.0" @@ -16967,15 +17062,15 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.3.tgz", - "integrity": "sha512-TTAOZpkJ2YLxl7mVHWrNo3iDMEkYlva/kgFcXndqMgbo/AZUmmavEkdXV+hXtE4P8xdyEKRzalaFqZVuwIk/Nw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", + "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.1", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "globalthis": "^1.0.3", "which-builtin-type": "^1.1.3" }, @@ -17177,15 +17272,15 @@ "peer": true }, "node_modules/rimraf": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.1.tgz", - "integrity": "sha512-OfFZdwtd3lZ+XZzYP/6gTACubwFcHdLRqS9UX3UwpU2dnGQYkPFISRwvM3w9IiB2w7bW5qGo/uAwE4SmXXSKvg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", "dev": true, "dependencies": { - "glob": "^10.2.5" + "glob": "^10.3.7" }, "bin": { - "rimraf": "dist/cjs/src/bin.js" + "rimraf": "dist/esm/bin.mjs" }, "engines": { "node": ">=14" @@ -17204,19 +17299,19 @@ } }, "node_modules/rimraf/node_modules/glob": { - "version": "10.3.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", - "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", + "jackspeak": "^2.3.5", "minimatch": "^9.0.1", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", "path-scurry": "^1.10.1" }, "bin": { - "glob": "dist/cjs/src/bin.js" + "glob": "dist/esm/bin.mjs" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -17340,13 +17435,13 @@ } }, "node_modules/safe-array-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", - "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "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==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", + "get-intrinsic": "^1.2.1", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -17811,14 +17906,14 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/sirv": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", - "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.3.tgz", + "integrity": "sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==", "dev": true, "dependencies": { "@polka/url": "^1.0.0-next.20", "mrmime": "^1.0.0", - "totalist": "^1.0.0" + "totalist": "^3.0.0" }, "engines": { "node": ">= 10" @@ -18191,18 +18286,18 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/string.prototype.matchall": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", - "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.9.tgz", + "integrity": "sha512-6i5hL3MqG/K2G43mWXWgP+qizFW/QH/7kCNN13JrJS5q48FN5IKksLDscexKP3dnmB6cdm9jlNgAsWNLpSykmA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.3", + "internal-slot": "^1.0.5", + "regexp.prototype.flags": "^1.5.0", "side-channel": "^1.0.4" }, "funding": { @@ -18210,14 +18305,14 @@ } }, "node_modules/string.prototype.padend": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.4.tgz", - "integrity": "sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.5.tgz", + "integrity": "sha512-DOB27b/2UTTD+4myKUFh+/fXWcu/UDyASIXfg+7VzoCNNGOfWvoyU/x5pvVHr++ztyt/oSYI1BcWBBG/hmlNjA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -18227,14 +18322,14 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -18244,28 +18339,28 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "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==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "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.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -18391,9 +18486,9 @@ } }, "node_modules/terser": { - "version": "5.19.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", - "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", + "version": "5.19.4", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.4.tgz", + "integrity": "sha512-6p1DjHeuluwxDXcuT9VR8p64klWJKo1ILiy19s6C9+0Bh2+NWTX6nD9EPppiER4ICkHDVB1RkVpin/YW2nQn/g==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -18630,9 +18725,9 @@ "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" }, "node_modules/totalist": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", - "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", "dev": true, "engines": { "node": ">=6" @@ -18682,9 +18777,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.2.tgz", - "integrity": "sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", "dev": true, "engines": { "node": ">=16.13.0" @@ -19830,20 +19925,27 @@ } }, "node_modules/webpack-bundle-analyzer": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.0.tgz", - "integrity": "sha512-+bXGmO1LyiNx0i9enBu3H8mv42sj/BJWhZNFwjz92tVnBa9J3JMGo2an2IXlEleoDOPn/Hofl5hr/xCpObUDtw==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.1.tgz", + "integrity": "sha512-jnd6EoYrf9yMxCyYDPj8eutJvtjQNp8PHmni/e/ulydHBWhT5J3menXt3HEkScsu9YqMAcG4CfFjs3rj5pVU1w==", "dev": true, "dependencies": { "@discoveryjs/json-ext": "0.5.7", "acorn": "^8.0.4", "acorn-walk": "^8.0.0", - "chalk": "^4.1.0", "commander": "^7.2.0", + "escape-string-regexp": "^4.0.0", "gzip-size": "^6.0.0", - "lodash": "^4.17.20", + "is-plain-object": "^5.0.0", + "lodash.debounce": "^4.0.8", + "lodash.escape": "^4.0.1", + "lodash.flatten": "^4.4.0", + "lodash.invokemap": "^4.6.0", + "lodash.pullall": "^4.2.0", + "lodash.uniqby": "^4.7.0", "opener": "^1.5.2", - "sirv": "^1.0.7", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", "ws": "^7.3.1" }, "bin": { @@ -19853,55 +19955,6 @@ "node": ">= 10.13.0" } }, - "node_modules/webpack-bundle-analyzer/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/webpack-bundle-analyzer/node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -19911,25 +19964,16 @@ "node": ">= 10" } }, - "node_modules/webpack-bundle-analyzer/node_modules/has-flag": { + "node_modules/webpack-bundle-analyzer/node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" + "node": ">=10" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/webpack-bundle-analyzer/node_modules/ws": { @@ -20573,9 +20617,9 @@ } }, "node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.1.tgz", + "integrity": "sha512-4OOseMUq8AzRBI/7SLMUwO+FEDnguetSk7KMb1sHwvF2w2Wv5Hoj0nlifx8vtGsftE/jWHojPy8sMMzYLJ2G/A==", "dev": true, "engines": { "node": ">=10.0.0" diff --git a/package.json b/package.json index 92784b2e5..7e9d1e101 100644 --- a/package.json +++ b/package.json @@ -54,21 +54,21 @@ "@patternfly/react-tokens": "4.94.6", "@redhat-cloud-services/frontend-components": "^3.11.5", "@redhat-cloud-services/frontend-components-notifications": "^3.2.16", - "@redhat-cloud-services/frontend-components-translations": "^3.2.6", + "@redhat-cloud-services/frontend-components-translations": "^3.2.7", "@redhat-cloud-services/frontend-components-utilities": "^3.7.6", - "@redhat-cloud-services/rbac-client": "^1.2.6", + "@redhat-cloud-services/rbac-client": "^1.2.9", "@unleash/proxy-client-react": "^3.6.0", - "axios": "^1.5.0", + "axios": "^1.5.1", "date-fns": "^2.30.0", "js-file-download": "^0.4.12", "lodash": "^4.17.21", "qs": "^6.11.2", "react": "^18.2.0", - "react-bootstrap": "^2.8.0", + "react-bootstrap": "^2.9.0", "react-dom": "^18.2.0", - "react-intl": "^6.4.4", - "react-redux": "^8.1.2", - "react-router-dom": "^6.15.0", + "react-intl": "^6.4.7", + "react-redux": "^8.1.3", + "react-router-dom": "^6.16.0", "redux": "^4.2.1", "redux-thunk": "^2.4.2", "typesafe-actions": "^5.1.0", @@ -77,48 +77,48 @@ "xstate": "^4.38.2" }, "devDependencies": { - "@formatjs/cli": "^6.1.3", + "@formatjs/cli": "^6.2.0", "@redhat-cloud-services/eslint-config-redhat-cloud-services": "^1.3.0", "@redhat-cloud-services/frontend-components-config": "^5.1.1", "@testing-library/react": "^14.0.0", - "@testing-library/user-event": "^14.4.3", - "@types/jest": "^29.5.4", - "@types/qs": "^6.9.7", - "@types/react": "^18.2.21", - "@types/react-dom": "^18.2.7", - "@types/react-redux": "^7.1.26", + "@testing-library/user-event": "^14.5.1", + "@types/jest": "^29.5.5", + "@types/qs": "^6.9.8", + "@types/react": "^18.2.24", + "@types/react-dom": "^18.2.8", + "@types/react-redux": "^7.1.27", "@types/react-router-dom": "^5.3.3", - "@typescript-eslint/eslint-plugin": "^6.4.1", - "@typescript-eslint/parser": "^6.4.1", + "@typescript-eslint/eslint-plugin": "^6.7.3", + "@typescript-eslint/parser": "^6.7.3", "@xstate/test": "^0.5.1", "aphrodite": "^2.4.0", "copy-webpack-plugin": "^11.0.0", - "eslint": "^8.48.0", - "eslint-plugin-formatjs": "^4.10.3", + "eslint": "^8.50.0", + "eslint-plugin-formatjs": "^4.10.5", "eslint-plugin-jest-dom": "^5.1.0", - "eslint-plugin-jsdoc": "^46.5.0", + "eslint-plugin-jsdoc": "^46.8.2", "eslint-plugin-markdown": "^3.0.1", "eslint-plugin-patternfly-react": "^5.0.0", "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-react": "^7.33.2", "eslint-plugin-simple-import-sort": "^10.0.0", "eslint-plugin-sort-keys-fix": "^1.1.2", - "eslint-plugin-testing-library": "^6.0.1", + "eslint-plugin-testing-library": "^6.0.2", "git-revision-webpack-plugin": "^5.0.0", - "jest": "^29.6.4", - "jest-environment-jsdom": "^29.6.4", - "jest-mock-axios": "^4.7.2", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "jest-mock-axios": "^4.7.3", "jws": "^4.0.0", "npm-run-all": "^4.1.5", - "prettier": "^3.0.2", - "rimraf": "^5.0.1", + "prettier": "^3.0.3", + "rimraf": "^5.0.5", "ts-patch": "^3.0.2", "typescript": "^5.2.2", - "webpack-bundle-analyzer": "^4.9.0" + "webpack-bundle-analyzer": "^4.9.1" }, "overrides": { - "eslint": "^8.48.0", - "react-intl": "^6.4.4" + "eslint": "^8.50.0", + "react-intl": "^6.4.7" }, "insights": { "appname": "cost-management" diff --git a/scripts/release-branch.sh b/scripts/release-branch.sh index 05467ac77..8f59d8871 100755 --- a/scripts/release-branch.sh +++ b/scripts/release-branch.sh @@ -17,6 +17,8 @@ default() UI_DIR="$TMP_DIR/koku-ui" UI_REPO="git@github.com:project-koku/koku-ui.git" + + BODY_FILE="$UI_DIR/body" } usage() @@ -49,6 +51,14 @@ clone() git clone $UI_REPO } +createPullRequestBody() +{ +cat <<- EEOOFF > $BODY_FILE +Merged $REMOTE_BRANCH branch to $BRANCH. + +Use latest commit to update namespace \`ref\` in app-interface repo. Don't use merge commit, SHAs must be unique when images are created for each branch. +EEOOFF +} merge() { @@ -67,7 +77,7 @@ merge() # Use gh in a non-interactive way -- see https://github.com/cli/cli/issues/1718 pullRequest() { - NEW_BRANCH="release/${BRANCH}.$$" + NEW_BRANCH="release_${BRANCH}.$$" git branch -m $NEW_BRANCH @@ -75,7 +85,7 @@ pullRequest() git push -u origin HEAD TITLE="Deployment commit for $BRANCH" - BODY="Merged $REMOTE_BRANCH branch to $BRANCH. Use latest commit to update namespace \`ref\` in app-interface repo. Don't use merge commit, SHAs must be unique when images are created for each branch." + BODY=`cat $BODY_FILE` gh pr create -t "$TITLE" -b "$BODY" -B $BRANCH } @@ -124,6 +134,7 @@ push() if [ -n "$PUSH" ]; then push else + createPullRequestBody pullRequest fi else diff --git a/src/api/providers.ts b/src/api/providers.ts index 442278ad7..fed633825 100644 --- a/src/api/providers.ts +++ b/src/api/providers.ts @@ -47,6 +47,7 @@ export interface Provider { has_data?: boolean; infrastructure?: ProviderInfrastructure; name?: string; + paused?: boolean; previous_month_data?: boolean; source_type?: string; type?: string; diff --git a/src/components/drawers/commonDrawer/commonDrawer.scss b/src/components/drawers/commonDrawer/commonDrawer.scss deleted file mode 100644 index 9c7ecdcba..000000000 --- a/src/components/drawers/commonDrawer/commonDrawer.scss +++ /dev/null @@ -1,5 +0,0 @@ -.drawerOverride { - .pf-c-drawer__content { - background-color: unset; - } -} diff --git a/src/components/drawers/commonDrawer/commonDrawer.tsx b/src/components/drawers/commonDrawer/commonDrawer.tsx index 528819eca..fcd76d342 100644 --- a/src/components/drawers/commonDrawer/commonDrawer.tsx +++ b/src/components/drawers/commonDrawer/commonDrawer.tsx @@ -1,6 +1,4 @@ -import './commonDrawer.scss'; - -import { Drawer, DrawerContent, DrawerContentBody } from '@patternfly/react-core'; +import { Drawer, DrawerContent } from '@patternfly/react-core'; import { ExportsDrawer } from 'components/drawers'; import { OptimizationsDrawer } from 'components/drawers'; import React from 'react'; @@ -56,8 +54,8 @@ class CommonDrawerBase extends React.Component { // https://github.com/redhat-developer/rhosak-ui/blob/main/packages/ui/src/components/KafkaInstanceDrawer/KafkaInstanceDrawer.tsx#L69-L78 return ( - - {children} + + {children} ); diff --git a/src/components/featureFlags/featureFlags.tsx b/src/components/featureFlags/featureFlags.tsx index d005deb5f..193c42b4a 100644 --- a/src/components/featureFlags/featureFlags.tsx +++ b/src/components/featureFlags/featureFlags.tsx @@ -6,14 +6,11 @@ import { featureFlagsActions } from 'store/featureFlags'; // eslint-disable-next-line no-shadow export const enum FeatureToggle { - costCategories = 'cost-management.ui.cost-categories', // AWS cost categories https://issues.redhat.com/browse/COST-3611 - costDistribution = 'cost-management.ui.cost-distribution', // OCP distributed overhead costs https://issues.redhat.com/browse/COST-3681 exports = 'cost-management.ui.exports', // Async exports https://issues.redhat.com/browse/COST-2223 finsights = 'cost-management.ui.finsights', // RHEL support for FINsights https://issues.redhat.com/browse/COST-3306 ibm = 'cost-management.ui.ibm', // IBM https://issues.redhat.com/browse/COST-935 ros = 'cost-management.ui.ros', // ROS support https://issues.redhat.com/browse/COST-3477 rosBeta = 'cost-management.ui.ros-beta', // ROS support https://issues.redhat.com/browse/COST-3477 - settings = 'cost-management.ui.settings', // Settings page https://issues.redhat.com/browse/COST-3307 settingsPlatform = 'cost-management.ui.settings.platform', // Platform projects https://issues.redhat.com/browse/COST-3818 } @@ -55,15 +52,12 @@ const useFeatureFlags = () => { await updateContext({ userId }).then(() => { dispatch( featureFlagsActions.setFeatureFlags({ - isCostCategoriesFeatureEnabled: client.isEnabled(FeatureToggle.costCategories), - isCostDistributionFeatureEnabled: client.isEnabled(FeatureToggle.costDistribution), isExportsFeatureEnabled: client.isEnabled(FeatureToggle.exports), isFinsightsFeatureEnabled: client.isEnabled(FeatureToggle.finsights), isIbmFeatureEnabled: client.isEnabled(FeatureToggle.ibm), isRosFeatureEnabled: client.isEnabled(FeatureToggle.ros) || (client.isEnabled(FeatureToggle.rosBeta) && insights && insights.chrome && insights.chrome.isBeta()), - isSettingsFeatureEnabled: client.isEnabled(FeatureToggle.settings), isSettingsPlatformFeatureEnabled: client.isEnabled(FeatureToggle.settingsPlatform), }) ); diff --git a/src/components/inactiveSources/inactiveSources.tsx b/src/components/inactiveSources/inactiveSources.tsx index f088d943a..ab8d45faa 100644 --- a/src/components/inactiveSources/inactiveSources.tsx +++ b/src/components/inactiveSources/inactiveSources.tsx @@ -35,7 +35,7 @@ class InactiveSourcesBase extends React.Component { if (providers && providers.data) { providers.data.map(data => { - if (data.active !== true) { + if (data.active !== true && data.paused !== true) { sources.push(data.name); } }); diff --git a/src/components/pageTitle/pageTitle.tsx b/src/components/pageTitle/pageTitle.tsx index 9a9d0a732..9fd7f88a5 100644 --- a/src/components/pageTitle/pageTitle.tsx +++ b/src/components/pageTitle/pageTitle.tsx @@ -21,9 +21,7 @@ const PageTitleBase: React.FC = ({ children = null, intl }) => { case formatPath(routes.azureDetails.path): case formatPath(routes.azureDetailsBreakdown.path): return messages.pageTitleAzure; - case formatPath(routes.costModel.path): - case formatPath(routes.costModelsDetails.path): - case formatPath(routes.costModels.path): + case formatPath(routes.costModel.basePath): return messages.pageTitleCostModels; case formatPath(routes.explorer.path): return messages.pageTitleExplorer; @@ -46,6 +44,8 @@ const PageTitleBase: React.FC = ({ children = null, intl }) => { case formatPath(routes.rhelDetails.path): case formatPath(routes.rhelDetailsBreakdown.path): return messages.pageTitleRhel; + case formatPath(routes.settings.path): + return messages.pageTitleSettings; default: return messages.pageTitleDefault; } diff --git a/src/components/permissions/permissions.tsx b/src/components/permissions/permissions.tsx index 6c43db379..ffb88f655 100644 --- a/src/components/permissions/permissions.tsx +++ b/src/components/permissions/permissions.tsx @@ -35,7 +35,6 @@ interface PermissionsStateProps { isFinsightsFeatureEnabled?: boolean; isIbmFeatureEnabled?: boolean; isRosFeatureEnabled?: boolean; - isSettingsFeatureEnabled?: boolean; userAccess: UserAccess; userAccessError: AxiosError; userAccessFetchStatus: FetchStatus; @@ -50,7 +49,6 @@ const PermissionsBase: React.FC = ({ isFinsightsFeatureEnabled, isIbmFeatureEnabled, isRosFeatureEnabled, - isSettingsFeatureEnabled, userAccess, userAccessError, userAccessFetchStatus, @@ -69,7 +67,7 @@ const PermissionsBase: React.FC = ({ const ocp = hasOcpAccess(userAccess); const rhel = isFinsightsFeatureEnabled && hasRhelAccess(userAccess); const ros = isRosFeatureEnabled && hasRosAccess(userAccess); - const settings = (costModel || hasSettingsAccess(userAccess)) && isSettingsFeatureEnabled; + const settings = costModel || hasSettingsAccess(userAccess); switch (pathname) { case formatPath(routes.explorer.path): @@ -82,7 +80,6 @@ const PermissionsBase: React.FC = ({ case formatPath(routes.azureDetailsBreakdown.path): return azure; case formatPath(routes.costModel.basePath): - case formatPath(routes.costModelsDetails.path): return costModel; case formatPath(routes.gcpDetails.path): case formatPath(routes.gcpDetailsBreakdown.path): @@ -137,7 +134,6 @@ const mapStateToProps = createMapStateToProps import(/* webpackChunkName: "awsBreakdown" */ 'r const AwsDetails = lazy(() => import(/* webpackChunkName: "awsDetails" */ 'routes/details/awsDetails')); const AzureBreakdown = lazy(() => import(/* webpackChunkName: "azureBreakdown" */ 'routes/details/azureBreakdown')); const AzureDetails = lazy(() => import(/* webpackChunkName: "azureDetails" */ 'routes/details/azureDetails')); -const CostModelOld = lazy(() => import(/* webpackChunkName: "costModel" */ 'routes/costModels/costModel')); -const CostModelsDetailsOld = lazy(() => import(/* lazy: "costModelsDetails" */ 'routes/costModels/costModelsDetails')); const CostModel = lazy(() => import(/* webpackChunkName: "costModel" */ 'routes/settings/costModels/costModel')); const Explorer = lazy(() => import(/* webpackChunkName: "explorer" */ 'routes/explorer')); const GcpBreakdown = lazy(() => import(/* webpackChunkName: "gcpBreakdown" */ 'routes/details/gcpBreakdown')); @@ -49,17 +47,6 @@ const routes = { element: userAccess(CostModel), path: `/settings/cost-model/:uuid`, }, - // Todo: Remove when settings page is enabled - costModels: { - // Note: Order matters here (i.e., dynamic segment must be defined after costModelsDetails) - element: userAccess(CostModelOld), - path: `/cost-models/:uuid`, - }, - // Todo: Remove when settings page is enabled - costModelsDetails: { - element: userAccess(CostModelsDetailsOld), - path: '/cost-models', - }, explorer: { element: userAccess(Explorer), path: '/explorer', diff --git a/src/routes/components/groupBy/groupBy.tsx b/src/routes/components/groupBy/groupBy.tsx index 60a85d8be..edc8311fb 100644 --- a/src/routes/components/groupBy/groupBy.tsx +++ b/src/routes/components/groupBy/groupBy.tsx @@ -17,7 +17,6 @@ import type { PerspectiveType } from 'routes/explorer/explorerUtils'; import { getDateRangeFromQuery } from 'routes/utils/dateRange'; import type { FetchStatus } from 'store/common'; import { createMapStateToProps } from 'store/common'; -import { featureFlagsSelectors } from 'store/featureFlags'; import { orgActions, orgSelectors } from 'store/orgs'; import { resourceActions, resourceSelectors } from 'store/resources'; import { tagActions, tagSelectors } from 'store/tags'; @@ -48,7 +47,6 @@ interface GroupByOwnProps extends RouterComponentProps, WrappedComponentProps { } interface GroupByStateProps { - isCostCategoriesFeatureEnabled?: boolean; orgReport?: Org; orgReportFetchStatus?: FetchStatus; orgQueryString?: string; @@ -211,7 +209,7 @@ class GroupByBase extends React.Component { }; private getGroupByOptions = (): GroupByOption[] => { - const { isCostCategoriesFeatureEnabled, options, orgReport, resourceReport, tagReport, intl } = this.props; + const { options, orgReport, resourceReport, tagReport, intl } = this.props; const allOptions = [...options]; if (orgReport && orgReport.data && orgReport.data.length > 0) { @@ -220,7 +218,7 @@ class GroupByBase extends React.Component { if (tagReport && tagReport.data && tagReport.data.length > 0) { allOptions.push(...groupByTagOptions); } - if (resourceReport && resourceReport.data && resourceReport.data.length > 0 && isCostCategoriesFeatureEnabled) { + if (resourceReport && resourceReport.data && resourceReport.data.length > 0) { allOptions.push(...groupByCostCategoryOptions); } return allOptions @@ -279,7 +277,6 @@ class GroupByBase extends React.Component { fetchOrg, fetchResource, fetchTag, - isCostCategoriesFeatureEnabled, orgPathsType, orgQueryString, showCostCategories, @@ -291,7 +288,7 @@ class GroupByBase extends React.Component { tagQueryString, } = this.props; - if (showCostCategories && isCostCategoriesFeatureEnabled) { + if (showCostCategories) { fetchResource(resourcePathsType, resourceType, resourceQueryString); } if (showOrgs) { @@ -415,7 +412,6 @@ const mapStateToProps = createMapStateToProps - {value, select, cpu {CPU} cluster {Cluster} memory {Memory} node {Node} persistent_volume_claims {Persistent volume claims} storage {Storage} other {}}{"value":"Node"} - , - - {value, select, cpu {CPU} cluster {Cluster} memory {Memory} node {Node} persistent_volume_claims {Persistent volume claims} storage {Storage} other {}}{"value":"CPU"} - , - - {value, select, cpu {CPU} cluster {Cluster} memory {Memory} node {Node} persistent_volume_claims {Persistent volume claims} storage {Storage} other {}}{"value":"CPU"} - , - - {value, select, cpu {CPU} cluster {Cluster} memory {Memory} node {Node} persistent_volume_claims {Persistent volume claims} storage {Storage} other {}}{"value":"CPU"} - , -] -`; diff --git a/src/routes/costModels/components/__snapshots__/warningIcon.test.tsx.snap b/src/routes/costModels/components/__snapshots__/warningIcon.test.tsx.snap deleted file mode 100644 index d0379a688..000000000 --- a/src/routes/costModels/components/__snapshots__/warningIcon.test.tsx.snap +++ /dev/null @@ -1,17 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`warning icon 1`] = ` - -`; diff --git a/src/routes/costModels/components/addPriceList.test.tsx b/src/routes/costModels/components/addPriceList.test.tsx deleted file mode 100644 index fc19d58bf..000000000 --- a/src/routes/costModels/components/addPriceList.test.tsx +++ /dev/null @@ -1,322 +0,0 @@ -import { act, configure, render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import type { Rate } from 'api/rates'; -import messages from 'locales/messages'; -import React from 'react'; -import { CostModelContext, defaultCostModelContext } from 'routes/costModels/costModelWizard/context'; - -import AddPriceList from './addPriceList'; - -const metricsHash = { - CPU: { - Request: { - source_type: 'Openshift Container Platform', - metric: 'cpu_core_request_per_hour', - label_metric: 'CPU', - label_measurement: 'Request', - label_measurement_unit: 'core-hours', - default_cost_type: 'Infrastructure', - }, - Usage: { - source_type: 'Openshift Container Platform', - metric: 'cpu_core_usage_per_hour', - label_metric: 'CPU', - label_measurement: 'Usage', - label_measurement_unit: 'core-hours', - default_cost_type: 'Infrastructure', - }, - }, - Memory: { - Request: { - source_type: 'Openshift Container Platform', - metric: 'memory_gb_request_per_hour', - label_metric: 'Memory', - label_measurement: 'Request', - label_measurement_unit: 'GB-hours', - default_cost_type: 'Supplementary', - }, - Usage: { - source_type: 'Openshift Container Platform', - metric: 'memory_gb_usage_per_hour', - label_metric: 'Memory', - label_measurement: 'Usage', - label_measurement_unit: 'GB-hours', - default_cost_type: 'Supplementary', - }, - }, - Cluster: { - Currency: { - source_type: 'Openshift Container Platform', - metric: 'cluster_cost_per_month', - label_metric: 'Cluster', - label_measurement: 'Currency', - label_measurement_unit: 'pvc-months', - default_cost_type: 'Infrastructure', - }, - }, -}; - -const qr = { - metric: '[data-ouia-component-id="metric"] button', - measurement: '[data-ouia-component-id="measurement"] button', - description: '#description', - infraradio: /infrastructure/i, - supplradio: /supplementary/i, - regular: '#regular-rate', - regularError: '#regular-rate-helper', - switch: 'Enter rate by tag', - tagKeyPlaceHolder: 'Enter a tag key', - descriptionNth: (id: number) => `#desc_${id}`, - defaultNth: (id: number) => `#isDefault_${id}`, - tagValueNth: (id: number) => `#tagValue_${id}`, - rateNth: (id: number) => `#rate_${id}`, -}; - -function RenderFormDataUI({ cancel, submit }) { - const memoryRate = { - metric: { - name: 'memory_gb_usage_per_hour', - label_metric: 'Memory', - label_measurement: 'Request', - label_measurement_unit: 'GB-hours', - }, - description: '', - tag_rates: { - tag_key: 'app', - tag_values: [ - { - unit: 'USD', - value: 1, - default: false, - tag_value: 'app1', - description: '', - }, - { - unit: 'USD', - value: 2.31, - default: false, - tag_value: 'app2', - description: '', - }, - ], - }, - cost_type: 'Supplementary', - }; - return ( - - - - ); -} - -function regExp(msg) { - return new RegExp(msg.defaultMessage); -} - -// Update testId accessor since data-testid is not passed to the parent component of Select -configure({ testIdAttribute: 'data-ouia-component-id' }); - -describe('add-a-new-rate', () => { - test('regular rate', async () => { - const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); - const submit = jest.fn(); - const cancel = jest.fn(); - let options = null; - render(); - - await user.type(screen.getByLabelText('Description'), 'regular rate test'); - - // select first option for metric - await act(async () => user.click(screen.getByLabelText('Select Metric'))); - options = await screen.findAllByRole('option'); - await user.click(options[0]); - - // select first option for measurement - await act(async () => user.click(screen.getByLabelText('Select Measurement'))); - options = await screen.findAllByRole('option'); - await user.click(options[0]); - - // make sure the default cost type is selected - expect(screen.getByLabelText(qr.infraradio)).toHaveProperty('checked', true); - - // selecting a different measurement does not reset cost type to default - await user.click(screen.getByLabelText(qr.supplradio)); - - await act(async () => user.click(screen.getByLabelText('Select Measurement'))); - options = await screen.findAllByRole('option'); - await user.click(options[1]); - - expect(screen.getByLabelText(qr.supplradio)).toHaveProperty('checked', true); - - // selecting metric will reset both measurement and cost type - await user.click(screen.getByLabelText(qr.infraradio)); - - await act(async () => user.click(screen.getByLabelText('Select Metric'))); - options = await screen.findAllByRole('option'); - await user.click(options[1]); - - expect(screen.getByText(regExp(messages.costModelsRequiredField))).not.toBeNull(); - - await act(async () => user.click(screen.getByLabelText('Select Measurement'))); - options = await screen.findAllByRole('option'); - await user.click(options[0]); - - expect(screen.getByLabelText(qr.supplradio)).toHaveProperty('checked', true); - await user.click(screen.getByLabelText(qr.infraradio)); - - const rateInput = screen.getByLabelText('Assign rate'); - - // setting rate to anything but a number - expect(screen.queryByText(regExp(messages.priceListNumberRate))).toBeNull(); - await user.type(rateInput, 'A'); - expect(screen.getByText(regExp(messages.priceListNumberRate))).not.toBeNull(); - - // setting rate to a negative number - validation is done on blur - await user.clear(rateInput); - await user.type(rateInput, '-12'); - expect(screen.getByText(regExp(messages.priceListPosNumberRate))).not.toBeNull(); - - // setting rate to a valid number - await user.clear(rateInput); - await user.type(rateInput, '0.2'); - expect(screen.queryByText(regExp(messages.priceListNumberRate))).toBeNull(); - - // making sure button is enabled - const createButton = screen.getByText(regExp(messages.createRate)); - expect(createButton.getAttribute('aria-disabled')).toBe('false'); - await user.click(createButton); - expect(submit).toHaveBeenCalled(); - }); - - test('tag rates', async () => { - const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); - const submit = jest.fn(); - const cancel = jest.fn(); - let options = null; - render(); - - await user.type(screen.getByLabelText('Description'), 'tag rate test'); - - await act(async () => user.click(screen.getByLabelText('Select Metric'))); - options = await screen.findAllByRole('option'); - await user.click(options[0]); - - await act(async () => user.click(screen.getByLabelText('Select Measurement'))); - options = await screen.findAllByRole('option'); - await user.click(options[0]); - - await user.click(screen.getByLabelText(regExp(messages.costModelsEnterTagRate))); - - // tag key is required validation - const tagKeyInput = screen.getByPlaceholderText(qr.tagKeyPlaceHolder); - await user.type(tagKeyInput, 'test'); - expect(screen.queryByText(regExp(messages.costModelsRequiredField))).toBeNull(); - await user.clear(tagKeyInput); - expect(screen.getByText(regExp(messages.costModelsRequiredField))).not.toBeNull(); - await user.type(tagKeyInput, 'openshift'); - expect(screen.queryByText(regExp(messages.costModelsRequiredField))).toBeNull(); - - // tag value is required validation - const tagValueInput = screen.getByPlaceholderText('Enter a tag value'); - await user.type(tagValueInput, 'test'); - expect(screen.queryByText(regExp(messages.costModelsRequiredField))).toBeNull(); - await user.clear(tagValueInput); - expect(screen.getByText(regExp(messages.costModelsRequiredField))).not.toBeNull(); - await user.type(tagValueInput, 'openshift'); - expect(screen.queryByText(regExp(messages.costModelsRequiredField))).toBeNull(); - - // rate must be a number - const tagRateInput = screen.getByLabelText('Assign rate'); - await user.type(tagRateInput, 'test'); - expect(screen.getByText(regExp(messages.priceListNumberRate))).not.toBeNull(); - - // rate is required - await user.clear(tagRateInput); - expect(screen.getByText(regExp(messages.costModelsRequiredField))).not.toBeNull(); - - // rate must be positive - await user.type(tagRateInput, '-0.23'); - expect(screen.getByText(regExp(messages.priceListPosNumberRate))).not.toBeNull(); - - // setting a valid rate - now form is valid and can be submitted - const createButton = screen.getByText(regExp(messages.createRate)); - expect(createButton.getAttribute('aria-disabled')).toBe('true'); - await user.clear(tagRateInput); - - await user.type(tagRateInput, '0.23'); - await user.type(screen.getByPlaceholderText('Enter a tag description'), 'default worker'); - expect(createButton.getAttribute('aria-disabled')).toBe('false'); - - // set tag to default - await user.click(screen.getByLabelText('Default')); - - // add a new rate disables the submit button - await user.click(screen.getByText(/add more tag values/i)); - expect(createButton.getAttribute('aria-disabled')).toBe('true'); - - await user.click(screen.getAllByRole('button', { name: /remove tag value/i })[1]); - expect(createButton.getAttribute('aria-disabled')).toBe('false'); - await user.click(createButton); - expect(submit).toHaveBeenCalled(); - }); - - test('tag rates duplicate tag key', async () => { - const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); - const submit = jest.fn(); - const cancel = jest.fn(); - let options = null; - render(); - - await act(async () => user.click(screen.getByLabelText('Select Metric'))); - options = await screen.findAllByRole('option'); - await user.click(options[1]); - - await act(async () => user.click(screen.getByLabelText('Select Measurement'))); - options = await screen.findAllByRole('option'); - await user.click(options[0]); - - await user.click(screen.getByLabelText(regExp(messages.costModelsEnterTagRate))); - - // tag key is duplicated - const tagKeyInput = screen.getByPlaceholderText(qr.tagKeyPlaceHolder); - await user.type(tagKeyInput, 'app'); - expect(screen.getByText(regExp(messages.priceListDuplicate))).not.toBeNull(); - - await user.type(tagKeyInput, '1'); - expect(screen.queryByText(regExp(messages.priceListDuplicate))).toBeNull(); - - // change measurement will set tag key as not duplicate - await user.type(tagKeyInput, '{backspace}'); - expect(screen.getByText(regExp(messages.priceListDuplicate))).not.toBeNull(); - - await act(async () => user.click(screen.getByLabelText('Select Measurement'))); - options = await screen.findAllByRole('option'); - await user.click(options[1]); - - expect(screen.queryByText(regExp(messages.priceListDuplicate))).toBeNull(); - - await act(async () => user.click(screen.getByLabelText('Select Measurement'))); - options = await screen.findAllByRole('option'); - await user.click(options[0]); - - expect(screen.getByText(regExp(messages.priceListDuplicate))).not.toBeNull(); - }); - - test('hide "enter tag rates" switch on Cluster metric', async () => { - const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); - const submit = jest.fn(); - const cancel = jest.fn(); - let options = null; - - await render(); - - await act(async () => user.click(screen.getByLabelText('Select Metric'))); - options = await screen.findAllByRole('option'); - await user.click(options[2]); - - await act(async () => user.click(screen.getByLabelText('Select Measurement'))); - options = await screen.findAllByRole('option'); - await user.click(options[0]); - expect(screen.queryAllByLabelText(regExp(messages.costModelsEnterTagRate))).toHaveLength(0); - }); -}); diff --git a/src/routes/costModels/components/addPriceList.tsx b/src/routes/costModels/components/addPriceList.tsx deleted file mode 100644 index 18e80d6b0..000000000 --- a/src/routes/costModels/components/addPriceList.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { - ActionGroup, - Button, - ButtonVariant, - Form, - Stack, - StackItem, - Text, - TextContent, - TextVariants, - Title, - TitleSizes, -} from '@patternfly/react-core'; -import type { MetricHash } from 'api/metrics'; -import { intl as defaultIntl } from 'components/i18n'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import type { RateFormData } from 'routes/costModels/components/rateForm'; -import { canSubmit as isReadyForSubmit, RateForm, useRateData } from 'routes/costModels/components/rateForm'; -import { CostModelContext } from 'routes/costModels/costModelWizard/context'; - -interface AddPriceListOwnProps { - cancel: () => void; - currencyUnits?: string; - metricsHash: MetricHash; - submitRate: (data: RateFormData) => void; -} - -type AddPriceListProps = AddPriceListOwnProps & WrappedComponentProps; - -const AddPriceList: React.FC = ({ - cancel, - currencyUnits, - intl = defaultIntl, // Default required for testing - metricsHash, - submitRate, -}) => { - const { tiers } = React.useContext(CostModelContext); - const rateFormData: any = useRateData(metricsHash, undefined, tiers); - const canSubmit = React.useMemo(() => isReadyForSubmit(rateFormData), [rateFormData.errors, rateFormData.rateKind]); - return ( - - - - {intl.formatMessage(messages.costModelsWizardCreatePriceList)} - - - - - {intl.formatMessage(messages.costModelsWizardPriceListMetric)} - - - -
- - -
- - - - - - -
- ); -}; - -export default injectIntl(AddPriceList); diff --git a/src/routes/costModels/components/errorState.tsx b/src/routes/costModels/components/errorState.tsx deleted file mode 100644 index def15a71b..000000000 --- a/src/routes/costModels/components/errorState.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import type { EmptyStateProps } from '@patternfly/react-core'; -import { - Button, - EmptyState, - EmptyStateBody, - EmptyStateIcon, - EmptyStateVariant, - Stack, - StackItem, - Title, - TitleSizes, -} from '@patternfly/react-core'; -import { ExclamationCircleIcon } from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon'; -import global_DangerColor_100 from '@patternfly/react-tokens/dist/js/global_danger_color_100'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; -import { createMapStateToProps } from 'store/common'; -import { sourcesSelectors } from 'store/sourceSettings'; - -interface OwnProps { - actionButton: React.ReactNode; - title: string; - description: React.ReactNode; -} -type ErrorEmptyProps = Pick; -type ErrorStateProps = ErrorEmptyProps & OwnProps; - -export const ErrorState: React.FC = ({ variant, actionButton, title, description }) => { - return ( - - - - {title} - - - - {description} - - - {actionButton} - - ); -}; - -interface SourcesErrorStateProps extends WrappedComponentProps { - onRefresh: () => void; -} - -export const SourceStepErrorStateBase: React.FC = ({ intl, onRefresh }) => { - const title = intl.formatMessage(messages.costModelsWizardSourceErrorTitle); - const description = intl.formatMessage(messages.costModelsWizardSourceErrorDesc, { - url: ( - - "Status Page" - - ), - }); - const actionButton = ; - return ( - - - - {intl.formatMessage(messages.costModelsWizardSourceTitle)} - - - - - - - ); -}; -const SourceStepErrorState = injectIntl(SourceStepErrorStateBase); -export { SourceStepErrorState }; - -interface SourcesModalErrorOwnProps { - // TBD... -} - -interface SourcesModalErrorStateProps { - query: any; -} - -interface SourcesModalErrorDispatchProps { - onRefresh: () => void; -} - -type SourcesModalErrorProps = SourcesModalErrorOwnProps & - SourcesModalErrorStateProps & - SourcesModalErrorDispatchProps & - WrappedComponentProps; - -export const SourcesModalErrorStateBase: React.FC = ({ intl, onRefresh }) => { - const title = intl.formatMessage(messages.costModelsAssignSourcesErrorTitle); - const description = intl.formatMessage(messages.costModelsAssignSourcesErrorDesc, { - url: ( - - "Status Page" - - ), - }); - const actionButton = ; - return ( - - ); -}; - -const mapStateToProps = createMapStateToProps(state => { - return { - query: sourcesSelectors.query(state), - }; -}); - -const mapDispatchToProps = (stateProps, dispatchProps): SourcesModalErrorDispatchProps => { - const { query } = stateProps; - const { fetch } = dispatchProps; - const searchTerm = Object.keys(query).reduce((acc, curr) => { - if (query[curr] === null) { - return acc; - } - if (acc === '') { - return `${curr}=${query[curr]}`; - } - return `${acc}&${curr}=${query[curr]}`; - }, ''); - - return { - onRefresh: () => fetch(searchTerm), - }; -}; - -export const SourcesModalErrorState = injectIntl( - connect(mapStateToProps, mapDispatchToProps)(SourcesModalErrorStateBase) -); diff --git a/src/routes/costModels/components/filterLogic.test.ts b/src/routes/costModels/components/filterLogic.test.ts deleted file mode 100644 index b4d844ab5..000000000 --- a/src/routes/costModels/components/filterLogic.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { addMultiValueQuery, addSingleValueQuery, removeMultiValueQuery, removeSingleValueQuery } from './filterLogic'; - -describe('add multi value query', () => { - it('add new query key', () => { - const q = {}; - expect(addMultiValueQuery(q)('name', 'ocp-cost')).toEqual({ - name: ['ocp-cost'], - }); - }); - it('add new value to query key', () => { - const q = { name: ['ocp-cost'] }; - expect(addMultiValueQuery(q)('name', 'aws-cost')).toEqual({ - name: ['ocp-cost', 'aws-cost'], - }); - }); - it('add new value to query key even if it exists', () => { - const q = { name: ['ocp-cost'] }; - expect(addMultiValueQuery(q)('name', 'ocp-cost')).toEqual({ - name: ['ocp-cost', 'ocp-cost'], - }); - }); -}); - -describe('add single value query', () => { - it('add new query key', () => { - const q = {}; - expect(addSingleValueQuery(q)('name', 'ocp-cost')).toEqual({ - name: 'ocp-cost', - }); - }); - it('update value to query key', () => { - const q = { name: 'ocp-cost' }; - expect(addSingleValueQuery(q)('name', 'aws-cost')).toEqual({ - name: 'aws-cost', - }); - }); -}); - -describe('remove single value query', () => { - it('remove single value for not existed key', () => { - const q = { name: 'ocp-cost' }; - expect(removeSingleValueQuery(q)('source_type', 'AWS')).toEqual({ - name: 'ocp-cost', - }); - }); - it('remove value from query key', () => { - const q = { name: 'ocp-cost' }; - expect(removeSingleValueQuery(q)('name', 'aws-cost')).toEqual({}); - }); -}); - -describe('remove multi value query', () => { - it('remove multi value for not existed key', () => { - const q = { name: 'ocp-cost' }; - expect(removeMultiValueQuery(q)('source_type', 'AWS')).toEqual({ - name: 'ocp-cost', - }); - }); - it('remove value from query key that has one value', () => { - const q = { name: ['ocp-cost'] }; - expect(removeMultiValueQuery(q)('name', 'ocp-cost')).toEqual({}); - }); - it('remove value from query key that has more than one value', () => { - const q = { name: ['ocp-cost', 'aws-cost'] }; - expect(removeMultiValueQuery(q)('name', 'ocp-cost')).toEqual({ - name: ['aws-cost'], - }); - }); -}); diff --git a/src/routes/costModels/components/filterLogic.ts b/src/routes/costModels/components/filterLogic.ts deleted file mode 100644 index 22364812a..000000000 --- a/src/routes/costModels/components/filterLogic.ts +++ /dev/null @@ -1,44 +0,0 @@ -export const addMultiValueQuery = query => (key, value) => ({ - ...query, - [key]: query[key] ? [...query[key], value] : [value], -}); - -export const addSingleValueQuery = query => (key, value) => ({ - ...query, - [key]: value, -}); - -export const removeMultiValueQuery = query => (key, value) => { - if (query[key] === undefined) { - return query; - } - const newSubQuery = query[key].filter(qval => qval !== value); - if (newSubQuery.length === 0) { - return Object.keys(query).reduce((acc, cur) => { - if (cur === key) { - return acc; - } - return { ...acc, [cur]: query[cur] }; - }, {}); - } - return { - ...query, - [key]: newSubQuery, - }; -}; - -export const removeSingleValueQuery = query => key => { - return Object.keys(query).reduce((acc, cur) => { - if (cur === key) { - return acc; - } - return { ...acc, [cur]: query[cur] }; - }, {}); -}; - -export const flatQueryValue = (name: string, value: string | string[]) => { - if (typeof value === 'string') { - return [{ name, value }]; - } - return value.map(vl => ({ name, value: vl })); -}; diff --git a/src/routes/costModels/components/forms/form.tsx b/src/routes/costModels/components/forms/form.tsx deleted file mode 100644 index 3efbaa7b2..000000000 --- a/src/routes/costModels/components/forms/form.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { FormProps as FormPF4Props } from '@patternfly/react-core'; -import { Form as FormPF4 } from '@patternfly/react-core'; -import React from 'react'; - -type FormProps = Omit; - -export const Form: React.ComponentType = ({ children, ...props }) => { - return ( - ) => event.preventDefault()} {...props}> - {children} - - ); -}; diff --git a/src/routes/costModels/components/hoc/withPriceListSearch.test.tsx b/src/routes/costModels/components/hoc/withPriceListSearch.test.tsx deleted file mode 100644 index a5e614325..000000000 --- a/src/routes/costModels/components/hoc/withPriceListSearch.test.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; - -import { WithPriceListSearch } from './withPriceListSearch'; - -test('with price list search', async () => { - const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); - render( - - {({ onClearAll, onRemove, onSelect, setSearch, search }) => { - return ( -
- - - - - - -
-
Primary: {search.primary || 'None'}
-
Metrics: {search.metrics[0] || 'None'}
-
Measurements: {search.measurements[0] || 'None'}
-
-
- ); - }} -
- ); - expect(screen.getByRole('main').outerHTML).toEqual( - `
Primary: metrics
Metrics: None
Measurements: None
` - ); - await user.click(screen.getByText('Select CPU')); - expect(screen.getByRole('main').outerHTML).toEqual( - `
Primary: metrics
Metrics: CPU
Measurements: None
` - ); - await user.click(screen.getByText('Remove CPU')); - expect(screen.getByRole('main').outerHTML).toEqual( - `
Primary: metrics
Metrics: None
Measurements: None
` - ); - await user.click(screen.getByText('Select request')); - expect(screen.getByRole('main').outerHTML).toEqual( - `
Primary: metrics
Metrics: None
Measurements: Request
` - ); - await user.click(screen.getByText('Remove request')); - expect(screen.getByRole('main').outerHTML).toEqual( - `
Primary: metrics
Metrics: None
Measurements: None
` - ); - await user.click(screen.getByText('Set search')); - expect(screen.getByRole('main').outerHTML).toEqual( - `
Primary: measurements
Metrics: Memory
Measurements: Usage
` - ); - await user.click(screen.getByText('Clear all')); - expect(screen.getByRole('main').outerHTML).toEqual( - `
Primary: measurements
Metrics: None
Measurements: None
` - ); -}); diff --git a/src/routes/costModels/components/hoc/withPriceListSearch.tsx b/src/routes/costModels/components/hoc/withPriceListSearch.tsx deleted file mode 100644 index 9da2477a3..000000000 --- a/src/routes/costModels/components/hoc/withPriceListSearch.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React from 'react'; -import { checkBoxLogic, deleteChip } from 'routes/costModels/components/logic/selectCheckbox'; - -export interface PriceListSearchQuery { - primary: string; - metrics?: string[]; - measurements?: string[]; -} - -export interface WithPriceListSearchPropsRender { - search: PriceListSearchQuery; - setSearch: (newSearch: PriceListSearchQuery) => void; - onRemove: (category: string, chip: string) => void; - onSelect: (key: string, value: string) => void; - onClearAll: () => void; -} - -export interface WithPriceListSearchProps { - initialFilters?: PriceListSearchQuery; - children: (props: WithPriceListSearchPropsRender) => JSX.Element; -} - -interface WithPriceListSearchState { - filters: PriceListSearchQuery; -} - -export class WithPriceListSearch extends React.Component { - constructor(props) { - super(props); - this.state = { filters: this.props.initialFilters }; - this.handleChange = this.handleChange.bind(this); - this.onRemove = this.onRemove.bind(this); - this.onSelect = this.onSelect.bind(this); - this.onClearAll = this.onClearAll.bind(this); - } - - public handleChange(newSearch: PriceListSearchQuery) { - this.setState({ - filters: { ...this.state.filters, ...newSearch }, - }); - } - - public onClearAll() { - this.setState({ - filters: { - ...this.state.filters, - metrics: [], - measurements: [], - }, - }); - } - - public onRemove(category: string, chip: string) { - this.setState({ - filters: { - ...this.state.filters, - [category]: deleteChip(this.state.filters[category], chip), - }, - }); - } - - public onSelect(key: string, value: string) { - this.setState({ - filters: { - ...this.state.filters, - [key]: checkBoxLogic(this.state.filters[key], value), - }, - }); - } - - public render() { - return this.props.children({ - onClearAll: this.onClearAll, - onRemove: this.onRemove, - onSelect: this.onSelect, - setSearch: this.handleChange, - search: this.state.filters, - }); - } -} diff --git a/src/routes/costModels/components/hoc/withStateMachine.test.tsx b/src/routes/costModels/components/hoc/withStateMachine.test.tsx deleted file mode 100644 index fbfd7bfce..000000000 --- a/src/routes/costModels/components/hoc/withStateMachine.test.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { Machine } from 'xstate'; - -import { WithStateMachine } from './withStateMachine'; - -test('with state machine', async () => { - const toggleMachine = Machine({ - initial: 'off', - states: { - off: { - on: { - TOGGLE: 'on', - }, - }, - on: { - on: { - TOGGLE: 'off', - }, - }, - }, - }); - - const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); - - render( - - {({ current, send }) => { - return ; - }} - - ); - const buttonNode = screen.getByRole('button'); - expect(buttonNode.outerHTML).toBe(''); - await user.click(buttonNode); - expect(buttonNode.outerHTML).toBe(''); -}); diff --git a/src/routes/costModels/components/hoc/withStateMachine.tsx b/src/routes/costModels/components/hoc/withStateMachine.tsx deleted file mode 100644 index 49117f21b..000000000 --- a/src/routes/costModels/components/hoc/withStateMachine.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { interpret } from 'xstate'; - -export interface WithStateMachineState { - current: any; -} - -export interface WithStateMachinePropsRender { - current: any; - send: (event: any) => void; -} - -export interface WithStateMachineProps { - machine: any; - children: (args: WithStateMachinePropsRender) => JSX.Element; -} - -export class WithStateMachine extends React.Component { - public service = null; - constructor(props) { - super(props); - this.service = interpret(props.machine).onTransition(current => this.setState({ current })); - this.state = { - current: props.machine.initialState, - }; - } - - public componentDidMount() { - this.service.start(); - } - - public componentWillUnmount() { - this.service.stop(); - } - - public render() { - const { current } = this.state; - const { send } = this.service; - return this.props.children({ current, send }); - } -} diff --git a/src/routes/costModels/components/inputs/rateInput.styles.ts b/src/routes/costModels/components/inputs/rateInput.styles.ts deleted file mode 100644 index 8cb06777c..000000000 --- a/src/routes/costModels/components/inputs/rateInput.styles.ts +++ /dev/null @@ -1,8 +0,0 @@ -import global_FontWeight_bold from '@patternfly/react-tokens/dist/js/global_FontWeight_bold'; -import type React from 'react'; - -export const styles = { - currency: { - fontWeight: global_FontWeight_bold.value as any, - }, -} as { [className: string]: React.CSSProperties }; diff --git a/src/routes/costModels/components/inputs/rateInput.tsx b/src/routes/costModels/components/inputs/rateInput.tsx deleted file mode 100644 index b64e695a9..000000000 --- a/src/routes/costModels/components/inputs/rateInput.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import type { MessageDescriptor } from '@formatjs/intl/src/types'; -import type { FormGroupProps, TextInputProps } from '@patternfly/react-core'; -import { FormGroup, InputGroup, InputGroupText, TextInput } from '@patternfly/react-core'; -import { intl as defaultIntl } from 'components/i18n'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { formatCurrencyRaw } from 'utils/format'; - -import { styles } from './rateInput.styles'; - -interface UniqueProps { - currencyUnits?: string; - label?: MessageDescriptor | string; - helperTextInvalid?: MessageDescriptor | string; -} - -type RateFormGroup = Pick; -type RateTextInput = Pick; -type RateInputBaseProps = RateFormGroup & RateTextInput & UniqueProps & WrappedComponentProps; - -const RateInputBase: React.FC = ({ - currencyUnits = 'USD', - fieldId, - helperTextInvalid: helpText = messages.priceListPosNumberRate, - intl = defaultIntl, // Default required for testing - label = messages.rate, - onBlur, - onChange, - style, - validated, - value, -}) => { - const handleOnKeyDown = event => { - // Prevent 'enter' and '+' - if (event.keyCode === 13 || event.keyCode === 187) { - event.preventDefault(); - } - }; - return ( - - - - {intl.formatMessage(messages.currencyUnits, { units: currencyUnits })} - - - - - ); -}; - -const RateInput = injectIntl(RateInputBase); -export { RateInput }; diff --git a/src/routes/costModels/components/inputs/selector.tsx b/src/routes/costModels/components/inputs/selector.tsx deleted file mode 100644 index 7129c8a60..000000000 --- a/src/routes/costModels/components/inputs/selector.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import type { MessageDescriptor } from '@formatjs/intl/src/types'; -import type { FormGroupProps, FormSelectProps, SelectOptionObject } from '@patternfly/react-core'; -import { FormGroup, Select, SelectDirection, SelectOption, SelectVariant } from '@patternfly/react-core'; -import { intl as defaultIntl } from 'components/i18n'; -import React, { useEffect, useState } from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; - -interface SelectorFormGroupOwnProps { - helperTextInvalid?: MessageDescriptor | string; - isInvalid?: boolean; - label?: MessageDescriptor | string; - appendMenuTo?: HTMLElement | 'parent' | 'inline' | (() => HTMLElement); - toggleAriaLabel?: string; - maxHeight?: string | number; - placeholderText?: string; - direction?: SelectDirection.up | SelectDirection.down; - options: { - label: MessageDescriptor | string; - value: any; - description?: string; - }[]; -} - -interface SelectorOption extends SelectOptionObject { - toString(): string; // label - value?: string; - description?: string; -} - -type SelectorFormGroupProps = Pick; -type SelectorFormSelectProps = Pick< - FormSelectProps, - 'isDisabled' | 'value' | 'onChange' | 'aria-label' | 'id' | 'isRequired' ->; - -type SelectorProps = SelectorFormGroupOwnProps & - SelectorFormGroupProps & - SelectorFormSelectProps & - WrappedComponentProps; - -const SelectorBase: React.FC = ({ - 'aria-label': ariaLabel, - helperTextInvalid: helpText, - id, - intl = defaultIntl, // Default required for testing - toggleAriaLabel, - maxHeight, - placeholderText, - direction = SelectDirection.down, - isInvalid = false, - isRequired = false, - appendMenuTo = 'parent', - label, - value, - onChange, - options, - style, -}) => { - const [isOpen, setIsOpen] = useState(false); - const [selection, setSelection] = useState(null); - - useEffect(() => { - if (!value) { - setSelection(null); - } else { - setSelection(value); - } - }, [value]); - - const getSelectorOptions = (): SelectorOption[] => { - const ret = options.map(option => { - return { - toString: () => (typeof option.label === 'object' ? intl.formatMessage(option.label) : option.label), - value: option.value, - description: option.description, - } as SelectorOption; - }); - return ret; - }; - return ( - - - - ); -}; - -const Selector = injectIntl(SelectorBase); -export { Selector }; diff --git a/src/routes/costModels/components/inputs/simpleInput.tsx b/src/routes/costModels/components/inputs/simpleInput.tsx deleted file mode 100644 index 172e84822..000000000 --- a/src/routes/costModels/components/inputs/simpleInput.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import type { MessageDescriptor } from '@formatjs/intl/src/types'; -import type { FormGroupProps, TextInputProps } from '@patternfly/react-core'; -import { FormGroup, TextInput } from '@patternfly/react-core'; -import { intl as defaultIntl } from 'components/i18n'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; - -interface SimpleInputOwnProps { - helperTextInvalid?: MessageDescriptor | string; - label?: MessageDescriptor | string; -} - -type SimpleInputFormGroupProps = Pick; -type SimpleInputTextInputProps = Pick; -type SimpleInputProps = SimpleInputOwnProps & - SimpleInputTextInputProps & - SimpleInputFormGroupProps & - WrappedComponentProps; - -const SimpleInputBase: React.FC = ({ - id, - intl = defaultIntl, // Default required for testing - label, - isRequired, - helperTextInvalid: helpText, - onChange, - onBlur, - placeholder, - style, - validated, - value, -}) => { - return ( - - - - ); -}; - -const SimpleInput = injectIntl(SimpleInputBase); -export { SimpleInput }; diff --git a/src/routes/costModels/components/logic/__snapshots__/selectStateMachine.test.tsx.snap b/src/routes/costModels/components/logic/__snapshots__/selectStateMachine.test.tsx.snap deleted file mode 100644 index b869cdd84..000000000 --- a/src/routes/costModels/components/logic/__snapshots__/selectStateMachine.test.tsx.snap +++ /dev/null @@ -1,23 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` 1`] = ` -[ - { - "selection": [], - }, - [ - "collapsed", - ], -] -`; - -exports[`selectmachine via TOGGLE: reaches state: "#(machine).expanded" ({"selection":[]}) 1`] = ` -[ - { - "selection": [], - }, - [ - "expanded", - ], -] -`; diff --git a/src/routes/costModels/components/logic/selectCheckbox.test.ts b/src/routes/costModels/components/logic/selectCheckbox.test.ts deleted file mode 100644 index 1c1a659c7..000000000 --- a/src/routes/costModels/components/logic/selectCheckbox.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { checkBoxLogic, deleteChip } from './selectCheckbox'; - -describe('select checkbox', () => { - it('deleteChip', () => { - expect(deleteChip(['user1', 'user2'], 'user1')).toEqual(['user2']); - expect(deleteChip(['user2'], 'user1')).toEqual(['user2']); - expect(deleteChip(['user2'], 'user2')).toEqual([]); - }); - it('checkbox logic', () => { - expect(checkBoxLogic([], 'user1')).toEqual(['user1']); - expect(checkBoxLogic(['user1'], 'user1')).toEqual([]); - expect(checkBoxLogic(['user1'], 'user2')).toEqual(['user1', 'user2']); - }); -}); diff --git a/src/routes/costModels/components/logic/selectCheckbox.ts b/src/routes/costModels/components/logic/selectCheckbox.ts deleted file mode 100644 index 0eda60828..000000000 --- a/src/routes/costModels/components/logic/selectCheckbox.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const checkBoxLogic = (current: string[], selection: string): string[] => { - return current.includes(selection) ? deleteChip(current, selection) : [...current, selection]; -}; - -export const deleteChip = (current: string[], selection: string): string[] => { - return current.filter(chip => chip !== selection); -}; diff --git a/src/routes/costModels/components/logic/selectStateMachine.test.tsx b/src/routes/costModels/components/logic/selectStateMachine.test.tsx deleted file mode 100644 index 9f8ed4634..000000000 --- a/src/routes/costModels/components/logic/selectStateMachine.test.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { createModel } from '@xstate/test'; -import type { Interpreter } from 'xstate'; -import { assign, interpret } from 'xstate'; - -import type { SelectMachineContext, SelectMachineEvents, SelectMachineStates } from './selectStateMachine'; -import { selectMachineState } from './selectStateMachine'; - -describe('selectmachine', () => { - const newMachine = selectMachineState.withConfig({ - actions: { - assignSelection: assign({ - selection: (_ctx, evt) => [evt.selection], - }), - }, - }); - - const s = interpret(newMachine).onTransition(stt => { - expect([stt.context, stt.toStrings()]).toMatchSnapshot(); - }) as Interpreter; - s.start(); - - const tModel = createModel(newMachine).withEvents({ - TOGGLE: { - exec: () => { - s.send({ type: 'TOGGLE' }); - }, - }, - SELECT: { - exec: () => { - s.send({ type: 'SELECT', selection: '23' }); - }, - }, - }); - - tModel.getShortestPathPlansTo('expanded').forEach(plan => { - plan.paths.forEach(path => { - it(`${path.description}: ${plan.description}`, () => { - path.test({}); - }); - }); - }); -}); diff --git a/src/routes/costModels/components/logic/selectStateMachine.ts b/src/routes/costModels/components/logic/selectStateMachine.ts deleted file mode 100644 index e2dddf128..000000000 --- a/src/routes/costModels/components/logic/selectStateMachine.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { MachineConfig } from 'xstate'; -import { Machine } from 'xstate'; - -export interface SelectMachineContext { - selection?: string[]; -} - -export interface SelectMachineStates { - states: { - collapsed: any; - expanded: any; - }; -} - -export type SelectMachineEvents = { type: 'TOGGLE'; selection?: string } | { type: 'SELECT'; selection: string }; - -export const selectMachineConfig: MachineConfig = { - context: { - selection: [], - }, - initial: 'collapsed', - states: { - collapsed: { - on: { - TOGGLE: 'expanded', - }, - }, - expanded: { - on: { - TOGGLE: 'collapsed', - SELECT: { - target: 'collapsed', - actions: ['assignSelection'], - }, - }, - }, - }, -}; - -export const selectMachineState = Machine(selectMachineConfig); diff --git a/src/routes/costModels/components/logic/types.ts b/src/routes/costModels/components/logic/types.ts deleted file mode 100644 index d92ad48c3..000000000 --- a/src/routes/costModels/components/logic/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface Option { - label: string; - value: string; -} diff --git a/src/routes/costModels/components/paginationToolbarTemplate.tsx b/src/routes/costModels/components/paginationToolbarTemplate.tsx deleted file mode 100644 index 495054063..000000000 --- a/src/routes/costModels/components/paginationToolbarTemplate.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import type { PaginationProps } from '@patternfly/react-core'; -import { Pagination, Toolbar, ToolbarContent, ToolbarItem } from '@patternfly/react-core'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; - -interface PaginationToolbarTemplateProps extends PaginationProps, WrappedComponentProps { - id?: string; -} - -export const PaginationToolbarTemplateBase: React.FC = ({ - id, - intl, - itemCount, - perPage, - page, - variant, - onPerPageSelect, - onSetPage, -}) => { - return ( - - - - - - - - ); -}; - -export const PaginationToolbarTemplate = injectIntl(PaginationToolbarTemplateBase); diff --git a/src/routes/costModels/components/priceListToolbar.styles.ts b/src/routes/costModels/components/priceListToolbar.styles.ts deleted file mode 100644 index 449d6e958..000000000 --- a/src/routes/costModels/components/priceListToolbar.styles.ts +++ /dev/null @@ -1,7 +0,0 @@ -import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; - -export const styles = { - toolbar: { - gridGap: global_spacer_md.value, - }, -}; diff --git a/src/routes/costModels/components/priceListToolbar.test.tsx b/src/routes/costModels/components/priceListToolbar.test.tsx deleted file mode 100644 index e6de2707c..000000000 --- a/src/routes/costModels/components/priceListToolbar.test.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import React from 'react'; - -import { PriceListToolbar } from './priceListToolbar'; - -test('price list toolbar', () => { - render( - Add rate} - primary={
Primary selector
} - selected={'sec2'} - secondaries={[ - { - component:
Secondary 1
, - name: 'sec1', - onRemove: jest.fn(), - filters: ['item1', 'item2'], - }, - { - component:
Secondary 2
, - name: 'sec2', - onRemove: jest.fn(), - filters: ['version3'], - }, - ]} - onClear={jest.fn()} - pagination={
Pagination
} - /> - ); - expect(screen.queryAllByText('Primary selector').length).toBe(1); - expect(screen.queryAllByText('Secondary 2').length).toBe(1); - expect(screen.queryAllByText('Secondary 1').length).toBe(0); - expect(screen.queryAllByText('Pagination').length).toBe(1); - expect(screen.queryAllByText(/sec\d/).length).toBe(2); - expect(screen.queryAllByText(/item\d/).length).toBe(2); - expect(screen.queryAllByText(/version\d/).length).toBe(1); -}); diff --git a/src/routes/costModels/components/priceListToolbar.tsx b/src/routes/costModels/components/priceListToolbar.tsx deleted file mode 100644 index 0a56d92cd..000000000 --- a/src/routes/costModels/components/priceListToolbar.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { - Toolbar, - ToolbarContent, - ToolbarFilter, - ToolbarGroup, - ToolbarItem, - ToolbarItemVariant, - ToolbarToggleGroup, -} from '@patternfly/react-core'; -import { FilterIcon } from '@patternfly/react-icons/dist/esm/icons/filter-icon'; -import React from 'react'; - -import { styles } from './priceListToolbar.styles'; - -interface PriceListToolbarProps { - primary: React.ReactNode; - selected: string; - secondaries: { - component: React.ReactNode; - name: string; - onRemove: (category: string, chip: string) => void; - filters: string[]; - }[]; - onClear: () => void; - pagination: React.ReactNode; - button: React.ReactNode; -} - -export const PriceListToolbar: React.FC = ({ - primary, - secondaries, - pagination, - button, - onClear, - selected, -}) => { - return ( - - - }> - - {primary} - {secondaries.map(secondary => { - return ( - - - {selected === secondary.name ? secondary.component : ''} - - - ); - })} - - - {button} - {pagination} - -
-
- ); -}; diff --git a/src/routes/costModels/components/rateForm/canSubmit.tsx b/src/routes/costModels/components/rateForm/canSubmit.tsx deleted file mode 100644 index 5f391ac0a..000000000 --- a/src/routes/costModels/components/rateForm/canSubmit.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { RateFormData } from './utils'; - -export function canSubmit(rateFormData: RateFormData): boolean { - if (rateFormData.rateKind === 'tagging') { - return ( - rateFormData.errors.description === null && - rateFormData.errors.measurement === null && - rateFormData.errors.tagValues.every(err => err === null) && - rateFormData.errors.tagValueValues.every(err => err === null) && - rateFormData.errors.tagDescription.every(err => err === null) && - rateFormData.errors.tagKey === null - ); - } - return ( - rateFormData.errors.description === null && - rateFormData.errors.measurement === null && - rateFormData.errors.tieredRates === null - ); -} diff --git a/src/routes/costModels/components/rateForm/constants.ts b/src/routes/costModels/components/rateForm/constants.ts deleted file mode 100644 index aeffb5d08..000000000 --- a/src/routes/costModels/components/rateForm/constants.ts +++ /dev/null @@ -1,11 +0,0 @@ -import messages from 'locales/messages'; - -export const textHelpers = { - description_too_long: messages.costModelsDescTooLong, - duplicate: messages.priceListDuplicate, - not_number: messages.priceListNumberRate, - not_positive: messages.priceListPosNumberRate, - rate_too_long: messages.costModelsRateTooLong, - required: messages.costModelsRequiredField, - tag_too_long: messages.costModelsInfoTooLong, -}; diff --git a/src/routes/costModels/components/rateForm/hasDiff.ts b/src/routes/costModels/components/rateForm/hasDiff.ts deleted file mode 100644 index 681581e22..000000000 --- a/src/routes/costModels/components/rateForm/hasDiff.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { Rate } from 'api/rates'; - -import type { RateFormData } from './utils'; - -export function hasDiff(rate: Rate, rateFormData: RateFormData): boolean { - if (!rate) { - return true; - } - if (rate.description !== rateFormData.description) { - return true; - } - if (rate.metric.label_metric !== rateFormData.metric) { - return true; - } - if (rate.metric.label_measurement !== rateFormData.measurement.value) { - return true; - } - if (rate.cost_type !== rateFormData.calculation) { - return true; - } - const rateKind = rate.tiered_rates ? 'regular' : 'tagging'; - if (rateKind !== rateFormData.rateKind) { - return true; - } - if (rateKind === 'regular') { - if (Number(rate.tiered_rates[0].value) !== Number(rateFormData.tieredRates[0].value)) { - return true; - } - } - if (rateKind === 'tagging') { - const tr = rate.tag_rates; - if (tr.tag_key !== rateFormData.taggingRates.tagKey.value) { - return true; - } - if (tr.tag_values.length !== rateFormData.taggingRates.tagValues.length) { - return true; - } - const hasTagValuesDiff = tr.tag_values.some((tvalue, ix) => { - const cur = rateFormData.taggingRates.tagValues[ix]; - const isCurDefault = rateFormData.taggingRates.defaultTag === ix; - return ( - tvalue.tag_value !== cur.tagValue || - Number(tvalue.value) !== Number(cur.inputValue) || - tvalue.description !== cur.description || - tvalue.default !== isCurDefault - ); - }); - if (hasTagValuesDiff) { - return true; - } - } - return false; -} diff --git a/src/routes/costModels/components/rateForm/index.ts b/src/routes/costModels/components/rateForm/index.ts deleted file mode 100644 index 9e9ed9529..000000000 --- a/src/routes/costModels/components/rateForm/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { transformFormDataToRequest, mergeToRequest, genFormDataFromRate } from './utils'; -export { useRateData } from './useRateForm'; -export { RateForm } from './rateForm'; -export { canSubmit } from './canSubmit'; -export { hasDiff } from './hasDiff'; -export type { RateFormData } from './utils'; diff --git a/src/routes/costModels/components/rateForm/rateForm.tsx b/src/routes/costModels/components/rateForm/rateForm.tsx deleted file mode 100644 index 47220e2e7..000000000 --- a/src/routes/costModels/components/rateForm/rateForm.tsx +++ /dev/null @@ -1,243 +0,0 @@ -import { Button, ButtonVariant, FormGroup, Grid, GridItem, Radio, Switch } from '@patternfly/react-core'; -import { PlusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/plus-circle-icon'; -import type { MetricHash } from 'api/metrics'; -import { intl as defaultIntl } from 'components/i18n'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { RateInput } from 'routes/costModels/components/inputs/rateInput'; -import { Selector } from 'routes/costModels/components/inputs/selector'; -import { SimpleInput } from 'routes/costModels/components/inputs/simpleInput'; -import { unitsLookupKey } from 'utils/format'; - -import { TaggingRatesForm } from './taggingRatesForm'; -import type { UseRateData } from './useRateForm'; - -interface RateFormOwnProps { - currencyUnits?: string; - rateFormData: UseRateData; - metricsHash: MetricHash; -} - -type RateFormProps = RateFormOwnProps & WrappedComponentProps; - -// defaultIntl required for testing -const RateFormBase: React.FC = ({ currencyUnits, intl = defaultIntl, metricsHash, rateFormData }) => { - const { - addTag, - calculation, - description, - errors, - measurement: { value: measurement, isDirty: measurementDirty }, - metric, - rateKind, - removeTag, - setCalculation, - setDescription, - setMeasurement, - setMetric, - setRegular, - setTagKey, - step, - taggingRates: { - tagKey: { value: tagKey, isDirty: isTagKeyDirty }, - defaultTag, - tagValues, - }, - tieredRates: { - 0: { inputValue, isDirty: regularDirty }, - }, - toggleTaggingRate, - updateDefaultTag, - updateTag, - } = rateFormData; - const getMetricLabel = m => { - // Match message descriptor or default to API string - const value = m.replace(/ /g, '_').toLowerCase(); - return intl.formatMessage(messages.metricValues, { value }) || m; - }; - const getMeasurementLabel = (m, u) => { - // Match message descriptor or default to API string - const units = intl.formatMessage(messages.units, { units: unitsLookupKey(u) }) || u; - return ( - intl.formatMessage(messages.measurementValues, { - value: m.toLowerCase().replace('-', '_'), - units, - count: 2, - }) || m - ); - }; - const getMeasurementDescription = (o, u) => { - // Match message descriptor or default to API string - // units only works with Node, Cluster, and PVC. it does not need to be translated - // if the metric is CPU, Memory, or Storage, units will be like `core_hours` or `gb_hours` and must be translated - const units = u.toLowerCase().replace('-', '_'); - const desc = intl.formatMessage(messages.measurementValuesDesc, { - value: o.toLowerCase().replace('-', '_'), - units: units ? units : u, - }); - return desc ? desc : o; - }; - const metricOptions = React.useMemo(() => { - return Object.keys(metricsHash); - }, [metricsHash]); - const measurementOptions = React.useMemo(() => { - if (!metricOptions.includes(metric)) { - return []; - } - return Object.keys(metricsHash[metric]); - }, [metricOptions, metric]); - const style = { width: '360px' }; - const addStyle = { - paddingLeft: '0', - textAlign: 'left', - } as React.CSSProperties; - - return ( - <> - - - - { - return { - label: getMetricLabel(opt), - value: opt, - isDisabled: false, - }; - }), - ]} - /> - - {step === 'initial' ? null : ( - - { - const unit = metricsHash[metric][opt].label_measurement_unit; - return { - label: getMeasurementLabel(opt, unit), - value: opt, - isDisabled: false, - description: getMeasurementDescription(opt, unit), - }; - }), - ]} - /> - - )} - - {step === 'set_rate' ? ( - <> - <> - - setCalculation('Infrastructure')} - /> - setCalculation('Supplementary')} - /> - - {metric !== 'Cluster' ? ( - - ) : null} - - {rateKind === 'regular' ? ( - - ) : ( - <> - - - - - )} - - ) : null} - - ); -}; - -const RateForm = injectIntl(RateFormBase); -export { RateForm }; diff --git a/src/routes/costModels/components/rateForm/taggingRatesForm.tsx b/src/routes/costModels/components/rateForm/taggingRatesForm.tsx deleted file mode 100644 index 83fe85487..000000000 --- a/src/routes/costModels/components/rateForm/taggingRatesForm.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { Button, ButtonVariant, Checkbox, FormGroup, Split, SplitItem } from '@patternfly/react-core'; -import { MinusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/minus-circle-icon'; -import { intl as defaultIntl } from 'components/i18n'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { RateInput } from 'routes/costModels/components/inputs/rateInput'; -import { SimpleInput } from 'routes/costModels/components/inputs/simpleInput'; - -import type { UseRateData } from './useRateForm'; -import type { RateFormErrors, RateFormTagValue } from './utils'; - -interface TaggingRatesFormOwnProps { - currencyUnits?: string; - defaultTag: UseRateData['taggingRates']['defaultTag']; - errors: Pick; - removeTag: UseRateData['removeTag']; - tagValues: RateFormTagValue[]; - updateDefaultTag: UseRateData['updateDefaultTag']; - updateTag: UseRateData['updateTag']; -} - -type TaggingRatesFormProps = TaggingRatesFormOwnProps & WrappedComponentProps; - -const TaggingRatesFormBase: React.FC = ({ - currencyUnits, - defaultTag, - errors, - intl = defaultIntl, // Default required for testing - tagValues, - updateDefaultTag, - removeTag, - updateTag, -}) => { - const style = { width: '200px' }; - const elementStyle = { - height: '100%', - position: 'relative', - top: '50%', - } as React.CSSProperties; - return ( - <> - {tagValues.map((tag, ix: number) => { - return ( - - {intl.formatMessage(messages.equalsSymbol)} - - updateTag({ tagValue: value }, ix)} - validated={tagValues[ix].isTagValueDirty && errors.tagValueValues[ix] ? 'error' : 'default'} - helperTextInvalid={errors.tagValueValues[ix]} - /> - - - updateTag({ value }, ix)} - style={style} - validated={tagValues[ix].isDirty && errors.tagValues[ix] ? 'error' : 'default'} - value={tag.inputValue} - /> - - - updateTag({ description: value }, ix)} - helperTextInvalid={errors.tagDescription[ix]} - /> - - - - updateDefaultTag(ix)} /> - - - -  }> - - - - - ); - })} - - ); -}; - -const TaggingRatesForm = injectIntl(TaggingRatesFormBase); -export { TaggingRatesForm }; diff --git a/src/routes/costModels/components/rateForm/useRateForm.test.tsx b/src/routes/costModels/components/rateForm/useRateForm.test.tsx deleted file mode 100644 index 83bf18888..000000000 --- a/src/routes/costModels/components/rateForm/useRateForm.test.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { rateFormReducer } from './useRateForm'; -import { initialRateFormData } from './utils'; - -describe('do not update state scenarios', () => { - test('in the initial step discard UPDATE_MEASUREMENT', () => { - const state = rateFormReducer(undefined, { type: 'UPDATE_MEASUREMENT', value: 'Usage' }); - expect(state.measurement).toEqual(initialRateFormData.measurement); - }); - test('in the initial step discard UPDATE_CALCULATION', () => { - const state = rateFormReducer(undefined, { type: 'UPDATE_CALCULATION', value: 'Infrastructure' }); - expect(state.calculation).toEqual(initialRateFormData.calculation); - }); - test('unless step is set_rate discard TOGGLE_RATE_KIND', () => { - let state = rateFormReducer(undefined, { type: 'TOGGLE_RATE_KIND' }); - expect(state.rateKind).toEqual(initialRateFormData.rateKind); - state = rateFormReducer({ ...initialRateFormData, step: 'set_metric' }, { type: 'TOGGLE_RATE_KIND' }); - expect(state.rateKind).toEqual(initialRateFormData.rateKind); - }); - test('unless step is set_rate and rate kind is regular discard BLUR_REGULAR', () => { - let state = rateFormReducer(undefined, { type: 'BLUR_REGULAR' }); - expect(state.errors.tieredRates).toEqual(initialRateFormData.errors.tieredRates); - state = rateFormReducer( - { ...initialRateFormData, rateKind: 'regular', step: 'set_rate' }, - { type: 'TOGGLE_RATE_KIND' } - ); - expect(state.errors.tieredRates).toEqual(initialRateFormData.errors.tieredRates); - }); - test('unless step is set_rate and rate kind is tagging discard UPDATE_TAG_KEY', () => { - let state = rateFormReducer(undefined, { type: 'UPDATE_TAG_KEY', value: 'value!' }); - expect(state.taggingRates.tagKey).toEqual(initialRateFormData.taggingRates.tagKey); - state = rateFormReducer( - { ...initialRateFormData, rateKind: 'regular', step: 'set_rate' }, - { type: 'UPDATE_TAG_KEY', value: 'value!' } - ); - expect(state.taggingRates.tagKey).toEqual(initialRateFormData.taggingRates.tagKey); - }); - test('unless step is set_rate and rate kind is tagging discard UPDATE_TAG_DEFAULT', () => { - let state = rateFormReducer(undefined, { type: 'UPDATE_TAG_DEFAULT', index: 0 }); - expect(state.taggingRates.defaultTag).toEqual(initialRateFormData.taggingRates.defaultTag); - state = rateFormReducer( - { ...initialRateFormData, rateKind: 'regular', step: 'set_rate' }, - { type: 'UPDATE_TAG_DEFAULT', index: 0 } - ); - expect(state.taggingRates.tagKey).toEqual(initialRateFormData.taggingRates.tagKey); - }); - test('unless step is set_rate and rate kind is tagging discard BLUR_TAG_RATE', () => { - let state = rateFormReducer(undefined, { type: 'BLUR_TAG_RATE', index: 0 }); - expect(state.errors).toEqual(initialRateFormData.errors); - state = rateFormReducer( - { ...initialRateFormData, rateKind: 'regular', step: 'set_rate' }, - { type: 'BLUR_TAG_RATE', index: 0 } - ); - expect(state.errors).toEqual(initialRateFormData.errors); - }); - test('unless step is set_rate and rate kind is tagging discard UPDATE_TAG', () => { - let state = rateFormReducer(undefined, { type: 'UPDATE_TAG', index: 0, payload: { value: '20' } }); - expect(state.taggingRates).toEqual(initialRateFormData.taggingRates); - state = rateFormReducer( - { ...initialRateFormData, rateKind: 'regular', step: 'set_rate' }, - { type: 'UPDATE_TAG', index: 0, payload: { value: '20' } } - ); - expect(state.taggingRates).toEqual(initialRateFormData.taggingRates); - }); - test('unless step is set_rate and rate kind is tagging discard REMOVE_TAG', () => { - let state = rateFormReducer(undefined, { type: 'REMOVE_TAG', index: 1 }); - expect(state).toEqual(initialRateFormData); - const initial = { ...initialRateFormData, rateKind: 'regular', step: 'set_rate' }; - state = rateFormReducer(initial, { type: 'REMOVE_TAG', index: 1 }); - expect(state).toEqual(initial); - }); - test('unless step is set_rate and rate kind is tagging discard ADD_TAG', () => { - let state = rateFormReducer(undefined, { type: 'ADD_TAG' }); - expect(state).toEqual(initialRateFormData); - const initial = { ...initialRateFormData, rateKind: 'regular', step: 'set_rate' }; - state = rateFormReducer(initial, { type: 'ADD_TAG' }); - expect(state).toEqual(initial); - }); - test('discard any action that is not a valid type', () => { - const state = rateFormReducer(undefined, { type: 'BLAAAAA' }); - expect(state).toEqual(initialRateFormData); - }); -}); diff --git a/src/routes/costModels/components/rateForm/useRateForm.tsx b/src/routes/costModels/components/rateForm/useRateForm.tsx deleted file mode 100644 index dd74d0953..000000000 --- a/src/routes/costModels/components/rateForm/useRateForm.tsx +++ /dev/null @@ -1,321 +0,0 @@ -import type { MetricHash } from 'api/metrics'; -import type { Rate } from 'api/rates'; -import React from 'react'; -import { unFormat } from 'utils/format'; - -import { textHelpers } from './constants'; -import type { RateFormData, RateFormTagValue } from './utils'; -import { - descriptionErrors, - initialtaggingRates, - isDuplicateTagRate, - OtherTierFromRate, - OtherTierFromRateForm, - tagKeyValueErrors, -} from './utils'; -import { checkRateOnChange, genFormDataFromRate, getDefaultCalculation, initialRateFormData } from './utils'; - -type Actions = - | { type: 'ADD_TAG' } - | { type: 'REMOVE_TAG'; index: number } - | { - type: 'UPDATE_TAG'; - index: number; - payload: Partial; - } - | { type: 'UPDATE_DESCRIPTION'; value: string } - | { type: 'UPDATE_METRIC'; value: string; defaultCalculation: string } - | { type: 'UPDATE_MEASUREMENT'; value: string } - | { type: 'UPDATE_CALCULATION'; value: string } - | { type: 'UPDATE_REGULAR'; value: string } - | { type: 'UPDATE_TAG_KEY'; value: string } - | { type: 'UPDATE_TAG_DEFAULT'; index: number } - | { type: 'TOGGLE_RATE_KIND' } - | { type: 'RESET_FORM'; payload: RateFormData }; - -export function rateFormReducer(state = initialRateFormData, action: Actions) { - switch (action.type) { - case 'UPDATE_DESCRIPTION': - return { - ...state, - description: action.value, - errors: { - ...state.errors, - description: descriptionErrors(action.value), - }, - }; - case 'UPDATE_METRIC': { - const errors = state.errors; - const newMeasurement = state.measurement; - if (newMeasurement.isDirty) { - newMeasurement.value = ''; - // Past discussions, we've agreed this required error should show on measurement when metric updates - errors.measurement = textHelpers.required; - } - let step = state.step; - if (step === 'initial') { - step = 'set_metric'; - } - const newState = { - ...state, - metric: action.value, - measurement: newMeasurement, - errors, - step, - calculation: action.defaultCalculation, - rateKind: action.value === 'Cluster' ? 'regular' : state.rateKind, - }; - const cur = OtherTierFromRateForm(newState); - const duplicate = newState.otherTiers.find(val => isDuplicateTagRate(OtherTierFromRate(val), cur)); - return { - ...newState, - errors: { ...newState.errors, tagKey: duplicate ? textHelpers.duplicate : null }, - }; - } - case 'UPDATE_MEASUREMENT': { - if (state.step === 'initial') { - return state; - } - let step: string = state.step; - if (step === 'set_metric') { - step = 'set_rate'; - } - const newState = { - ...state, - measurement: { value: action.value, isDirty: true }, - errors: { ...state.errors, measurement: null }, - step, - }; - const cur = OtherTierFromRateForm(newState); - const duplicate = newState.otherTiers.find(val => isDuplicateTagRate(OtherTierFromRate(val), cur)); - return { - ...newState, - errors: { ...newState.errors, tagKey: duplicate ? textHelpers.duplicate : null }, - }; - } - case 'UPDATE_CALCULATION': { - if (state.step !== 'set_rate') { - return state; - } - const newState = { - ...state, - calculation: action.value, - }; - const cur = OtherTierFromRateForm(newState); - const duplicate = newState.otherTiers.find(val => isDuplicateTagRate(OtherTierFromRate(val), cur)); - return { - ...newState, - errors: { ...newState.errors, tagKey: duplicate ? textHelpers.duplicate : null }, - }; - } - case 'TOGGLE_RATE_KIND': { - if (state.step !== 'set_rate') { - return state; - } - return { - ...state, - rateKind: state.rateKind === 'regular' ? 'tagging' : 'regular', - }; - } - case 'UPDATE_REGULAR': { - return { - ...state, - tieredRates: [ - { - isDirty: true, - inputValue: action.value, - value: unFormat(action.value), // Normalize for API requests where USD decimal format is expected - }, - ], - errors: { - ...state.errors, - tieredRates: checkRateOnChange(action.value), - }, - }; - } - case 'UPDATE_TAG_KEY': { - if (state.step !== 'set_rate' || state.rateKind !== 'tagging') { - return state; - } - const newState = { - ...state, - taggingRates: { - ...state.taggingRates, - tagKey: { value: action.value, isDirty: true }, - }, - errors: { - ...state.errors, - tagKey: tagKeyValueErrors(action.value), - }, - }; - const cur = OtherTierFromRateForm(newState); - const duplicate = newState.otherTiers.find(val => isDuplicateTagRate(OtherTierFromRate(val), cur)); - return { - ...newState, - errors: { ...newState.errors, tagKey: duplicate ? textHelpers.duplicate : newState.errors.tagKey }, - }; - } - case 'UPDATE_TAG_DEFAULT': { - if (state.step !== 'set_rate' && state.rateKind !== 'tagging') { - return state; - } - return { - ...state, - taggingRates: { - ...state.taggingRates, - defaultTag: state.taggingRates.defaultTag === action.index ? null : action.index, - }, - }; - } - case 'UPDATE_TAG': { - if (state.step !== 'set_rate' || state.rateKind !== 'tagging') { - return state; - } - let error = state.errors.tagValues[action.index]; - let tagValueError = state.errors.tagValueValues[action.index]; - let descriptionError = state.errors.tagDescription[action.index]; - let isDirty = state.taggingRates.tagValues[action.index].isDirty; - let isTagValueDirty = state.taggingRates.tagValues[action.index].isTagValueDirty; - - if (action.payload.value !== undefined) { - const { value: rate } = action.payload; - error = checkRateOnChange(rate); - isDirty = true; - } - if (action.payload.tagValue !== undefined) { - tagValueError = tagKeyValueErrors(action.payload.tagValue); - isTagValueDirty = true; - } - if (action.payload.description !== undefined) { - descriptionError = descriptionErrors(action.payload.description); - } - return { - ...state, - taggingRates: { - ...state.taggingRates, - tagValues: [ - ...state.taggingRates.tagValues.slice(0, action.index), - { - ...state.taggingRates.tagValues[action.index], - ...action.payload, - ...(action.payload.value !== undefined && { - inputValue: action.payload.value, // Original user input - value: unFormat(action.payload.value), // Normalize for API requests where USD decimal format is expected - }), - isDirty, - isTagValueDirty, - }, - ...state.taggingRates.tagValues.slice(action.index + 1), - ], - }, - errors: { - ...state.errors, - tagDescription: [ - ...state.errors.tagDescription.slice(0, action.index), - descriptionError, - ...state.errors.tagDescription.slice(action.index + 1), - ], - tagValueValues: [ - ...state.errors.tagValueValues.slice(0, action.index), - tagValueError, - ...state.errors.tagValueValues.slice(action.index + 1), - ], - tagValues: [ - ...state.errors.tagValues.slice(0, action.index), - error, - ...state.errors.tagValues.slice(action.index + 1), - ], - // "Create rate" button must remain disabled if tag key not set -- see https://issues.redhat.com/browse/COST-3977 - tagKey: tagKeyValueErrors(state.taggingRates.tagKey.value), - }, - }; - } - case 'REMOVE_TAG': { - if (state.step !== 'set_rate' || state.rateKind !== 'tagging') { - return state; - } - return { - ...state, - errors: { - ...state.errors, - tagValues: [ - ...state.errors.tagValues.slice(0, action.index), - ...state.errors.tagValues.slice(action.index + 1), - ], - tagValueValues: [ - ...state.errors.tagValueValues.slice(0, action.index), - ...state.errors.tagValueValues.slice(action.index + 1), - ], - }, - taggingRates: { - ...state.taggingRates, - defaultTag: - state.taggingRates.defaultTag === action.index - ? null - : state.taggingRates.defaultTag > action.index - ? state.taggingRates.defaultTag - 1 - : state.taggingRates.defaultTag, - tagValues: [ - ...state.taggingRates.tagValues.slice(0, action.index), - ...state.taggingRates.tagValues.slice(action.index + 1), - ], - }, - }; - } - case 'ADD_TAG': { - if (state.step !== 'set_rate' || state.rateKind !== 'tagging') { - return state; - } - return { - ...state, - errors: { - ...state.errors, - tagValues: [...state.errors.tagValues, textHelpers.required], - tagDescription: [...state.errors.tagDescription, null], - }, - taggingRates: { - ...state.taggingRates, - tagValues: [...state.taggingRates.tagValues, { ...initialtaggingRates.tagValues[0] }], - }, - }; - } - case 'RESET_FORM': { - return action.payload; - } - default: { - return state; - } - } -} - -export type UseRateData = ReturnType; - -export function useRateData(metricsHash: MetricHash, rate: Rate = undefined, tiers: Rate[] = []) { - const initial = genFormDataFromRate(rate, undefined, tiers); - const [state, dispatch] = React.useReducer(rateFormReducer, initial); - return { - ...state, - reset: (payload: RateFormData) => dispatch({ type: 'RESET_FORM', payload }), - setDescription: (value: string) => dispatch({ type: 'UPDATE_DESCRIPTION', value }), - setMetric: (value: string) => - dispatch({ - type: 'UPDATE_METRIC', - value, - defaultCalculation: getDefaultCalculation(metricsHash, value), - }), - setMeasurement: (value: string) => - dispatch({ - type: 'UPDATE_MEASUREMENT', - value, - }), - setCalculation: (value: string) => dispatch({ type: 'UPDATE_CALCULATION', value }), - setRegular: (value: string) => dispatch({ type: 'UPDATE_REGULAR', value }), - toggleTaggingRate: () => dispatch({ type: 'TOGGLE_RATE_KIND' }), - setTagKey: (value: string) => dispatch({ type: 'UPDATE_TAG_KEY', value }), - removeTag: (index: number) => dispatch({ type: 'REMOVE_TAG', index }), - addTag: () => dispatch({ type: 'ADD_TAG' }), - updateTag: (payload: Partial<(typeof initialRateFormData)['taggingRates']['tagValues'][0]>, index: number) => - dispatch({ type: 'UPDATE_TAG', index, payload }), - updateDefaultTag: (index: number) => dispatch({ type: 'UPDATE_TAG_DEFAULT', index }), - }; -} diff --git a/src/routes/costModels/components/rateForm/utils.tsx b/src/routes/costModels/components/rateForm/utils.tsx deleted file mode 100644 index a4f7ad582..000000000 --- a/src/routes/costModels/components/rateForm/utils.tsx +++ /dev/null @@ -1,297 +0,0 @@ -import { SortByDirection } from '@patternfly/react-table'; -import type { CostModel, CostModelRequest } from 'api/costModels'; -import type { MetricHash } from 'api/metrics'; -import type { Rate, RateRequest, TagRates } from 'api/rates'; -import { countDecimals, formatCurrencyRateRaw, isCurrencyFormatValid, unFormat } from 'utils/format'; - -import { textHelpers } from './constants'; - -export const initialtaggingRates = { - tagKey: { - value: '', - isDirty: false, - }, - defaultTag: null, - tagValues: [ - { - tagValue: '', - description: '', - isDirty: false, - isTagValueDirty: false, - inputValue: '', - value: '', - }, - ], -}; - -export const initialRateFormData = { - otherTiers: [] as Rate[], - step: 'initial', - description: '', - metric: '', - measurement: { - value: '', - isDirty: false, - }, - calculation: '', - rateKind: 'regular', - tieredRates: [ - { - isDirty: false, - inputValue: '', - value: '', - }, - ], - taggingRates: { ...initialtaggingRates }, - errors: { - description: null, - measurement: textHelpers.required, - tieredRates: textHelpers.required, - tagValues: [textHelpers.required], - tagDescription: [null], - tagKey: textHelpers.required, - tagValueValues: [textHelpers.required], - }, -}; - -export type RateFormData = typeof initialRateFormData; -export type RateFormTagValue = (typeof initialRateFormData)['taggingRates']['tagValues'][0]; -export type taggingRates = (typeof initialRateFormData)['taggingRates']; -export type RateFormErrors = (typeof initialRateFormData)['errors']; - -export const checkRateOnChange = (inputValue: string) => { - if (inputValue.length === 0) { - return textHelpers.required; - } - if (!isCurrencyFormatValid(inputValue)) { - return textHelpers.not_number; - } - if (Number(unFormat(inputValue)) < 0) { - return textHelpers.not_positive; - } - // Test number of decimals - const decimals = countDecimals(inputValue); - if (decimals > 10) { - return textHelpers.rate_too_long; - } - return null; -}; - -export function getDefaultCalculation(metricsHash: MetricHash, metric: string) { - let options = Object.keys(metricsHash); - if (!options.includes(metric)) { - return ''; - } - options = Object.keys(metricsHash[metric]); - if (options.length === 0) { - return ''; - } - return metricsHash[metric][options[0]].default_cost_type; -} - -export function genFormDataFromRate(rate: Rate, defaultValue = initialRateFormData, tiers: Rate[]): RateFormData { - const otherTiers = tiers || defaultValue.otherTiers; - if (!rate) { - return { ...defaultValue, otherTiers }; - } - const rateKind = rate.tiered_rates ? 'regular' : 'tagging'; - let tieredRates = [{ inputValue: '', value: '', isDirty: true }]; - const tagRates = { ...initialtaggingRates }; - const errors = { - description: null, - measurement: null, - tieredRates: null, - tagValues: [null], - tagKey: null, - tagValueValues: [null], - tagDescription: [null], - }; - if (rateKind === 'tagging') { - const item = rate.tag_rates as TagRates; - tagRates.tagKey = { value: item.tag_key, isDirty: true }; - const defaultIndex = item.tag_values.findIndex(tvalue => tvalue.default); - tagRates.defaultTag = defaultIndex === -1 ? null : defaultIndex; - tagRates.tagValues = item.tag_values.map(tvalue => { - const value = formatCurrencyRateRaw(tvalue.value, tvalue.unit); - return { - description: tvalue.description, - inputValue: value, - isDirty: false, - isTagValueDirty: false, - tagValue: tvalue.tag_value, - value, - }; - }); - errors.tieredRates = textHelpers.required; - errors.tagValueValues = new Array(item.tag_values.length).fill(null); - errors.tagValues = new Array(item.tag_values.length).fill(null); - errors.tagDescription = new Array(item.tag_values.length).fill(null); - } - if (rateKind === 'regular') { - tieredRates = rate.tiered_rates.map(tieredRate => { - const value = formatCurrencyRateRaw(tieredRate.value, tieredRate.unit); - return { - inputValue: value, - isDirty: true, - value, - }; - }); - errors.tagValues = [textHelpers.required]; - errors.tagValueValues = [textHelpers.required]; - } - return { - otherTiers, - step: 'set_rate', - description: rate.description, - metric: rate.metric.label_metric, - measurement: { - value: rate.metric.label_measurement, - isDirty: true, - }, - calculation: rate.cost_type, - rateKind, - tieredRates, - taggingRates: tagRates, - errors, - }; -} - -export const mergeToRequest = ( - metricsHash: MetricHash, - costModel: CostModel, - rateFormData: RateFormData, - index: number = -1 -): CostModelRequest => { - if (index < 0) { - index = costModel.rates.length; - } - const rate = transformFormDataToRequest(rateFormData, metricsHash, costModel.currency) as RateRequest; - return { - currency: costModel.currency, - name: costModel.name, - source_type: 'OCP', - description: costModel.description, - distribution_info: { - distribution_type: costModel.distribution_info ? costModel.distribution_info.distribution_type : undefined, - platform_cost: costModel.distribution_info ? costModel.distribution_info.platform_cost : undefined, - worker_cost: costModel.distribution_info ? costModel.distribution_info.worker_cost : undefined, - }, - source_uuids: costModel.sources.map(src => src.uuid), - markup: { value: costModel.markup.value, unit: 'percent' }, - rates: [...costModel.rates.slice(0, index), rate, ...costModel.rates.slice(index + 1)], - }; -}; - -export const transformFormDataToRequest = ( - rateFormData: RateFormData, - metricsHash: MetricHash, - currencyUnits: string = 'USD' -): Rate => { - const ratesKey = rateFormData.rateKind === 'tagging' ? 'tag_rates' : 'tiered_rates'; - const ratesBody = - rateFormData.rateKind === 'tagging' - ? { - tag_key: rateFormData.taggingRates.tagKey.value, - tag_values: rateFormData.taggingRates.tagValues.map((tvalue, ix) => { - return { - tag_value: tvalue.tagValue, - unit: currencyUnits, - value: tvalue.value, - description: tvalue.description, - default: ix === rateFormData.taggingRates.defaultTag, - }; - }), - } - : rateFormData.tieredRates.map(tiered => { - return { - value: tiered.value, - unit: currencyUnits, - usage: { unit: currencyUnits }, - }; - }); - const metricData = metricsHash[rateFormData.metric][rateFormData.measurement.value]; - return { - description: rateFormData.description, - metric: { - metric: metricData.metric, - name: metricData.metric, - label_metric: metricData.label_metric, - label_measurement: metricData.label_measurement, - label_measurement_unit: metricData.label_measurement_unit, - source_type: 'OpenShift Cluster Platform', - default_cost_type: metricData.default_cost_type, - }, - cost_type: rateFormData.calculation, - [ratesKey]: ratesBody, - }; -}; - -export interface OtherTier { - metric: RateFormData['metric']; - measurement: RateFormData['measurement']['value']; - costType: RateFormData['calculation']; - tagKey: RateFormData['taggingRates']['tagKey']['value']; -} - -export const OtherTierFromRate = (rate: Rate): OtherTier => { - const tagKey = rate.tag_rates && rate.tag_rates.tag_key ? rate.tag_rates.tag_key : null; - return { - metric: rate.metric.label_metric, - measurement: rate.metric.label_measurement, - tagKey, - costType: rate.cost_type, - }; -}; - -export const OtherTierFromRateForm = (rateData: RateFormData): OtherTier => { - const tagKey = rateData.taggingRates && rateData.taggingRates.tagKey ? rateData.taggingRates.tagKey.value : null; - const res = { - metric: rateData.metric, - measurement: rateData.measurement ? rateData.measurement.value : null, - tagKey, - costType: rateData.calculation, - }; - return res; -}; - -export const isDuplicateTagRate = (rate: OtherTier, current: OtherTier) => { - return ( - rate.metric === current.metric && - rate.measurement === current.measurement && - rate.costType === current.costType && - rate.tagKey === current.tagKey - ); -}; - -export type CompareResult = 1 | -1 | 0; - -export function compareBy( - r1: Rate, - r2: Rate, - direction: keyof typeof SortByDirection, - projection: (r: Rate) => string -): CompareResult { - const m1 = projection(r1); - const m2 = projection(r2); - if (direction === SortByDirection.asc) { - return m1 > m2 ? 1 : m1 < m2 ? -1 : 0; - } - return m1 > m2 ? -1 : m1 < m2 ? 1 : 0; -} - -export const descriptionErrors = (value: string) => { - if (value.length > 500) { - return textHelpers.description_too_long; - } - return null; -}; - -export const tagKeyValueErrors = (value: string) => { - if (value.length === 0) { - return textHelpers.required; - } - if (value.length > 100) { - return textHelpers.tag_too_long; - } - return null; -}; diff --git a/src/routes/costModels/components/rateTable.test.tsx b/src/routes/costModels/components/rateTable.test.tsx deleted file mode 100644 index 1cadc0964..000000000 --- a/src/routes/costModels/components/rateTable.test.tsx +++ /dev/null @@ -1,177 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import type { Rate } from 'api/rates'; -import React from 'react'; - -import { RateTable } from './rateTable'; - -describe('rate-table', () => { - test('smoke-test', async () => { - const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); - const tiers: Rate[] = [ - { - description: 'rate 1', - metric: { - name: 'cpu_core_request_per_hour', - metric: 'cpu_core_request_per_hour', - source_type: 'openshift container platform', - default_cost_type: 'Supplementary', - label_metric: 'CPU', - label_measurement: 'Request', - label_measurement_unit: 'core-hour', - }, - cost_type: 'Infrastructure', - tiered_rates: [ - { - unit: 'USD', - value: 130.32, - usage: { - unit: 'USD', - }, - }, - ], - }, - { - description: 'rate 2', - metric: { - name: 'cpu_core_request_per_hour', - metric: 'cpu_core_request_per_hour', - source_type: 'openshift container platform', - label_metric: 'CPU', - label_measurement: 'Usage', - label_measurement_unit: 'core-hour', - default_cost_type: 'Supplementary', - }, - cost_type: 'Supplementary', - tag_rates: { - tag_key: 'openshift', - tag_values: [ - { - unit: 'USD', - value: 0.43, - tag_value: 'worker', - description: 'default', - default: true, - }, - { - unit: 'USD', - value: 1.5, - tag_value: 'grafana', - description: 'grafana containers', - default: false, - }, - ], - }, - }, - ]; - render(); - expect(screen.getByText('rate 1')).toBeTruthy(); - expect(screen.getByText('rate 2')).toBeTruthy(); - expect(screen.queryByText('grafana')).toBeNull(); - await user.click(screen.getByRole('button', { name: 'Various' })); - expect(screen.getByText('grafana')).toBeTruthy(); - }); - test('sort by metric & measurement', () => { - // eslint-disable-next-line no-console - console.error = jest.fn(); - const tiers: Rate[] = [ - { - description: '', - metric: { - name: 'node_cost_per_month', - metric: 'node_cost_per_month', - source_type: 'openshift container platform', - label_metric: 'Node', - label_measurement: 'Currency', - label_measurement_unit: 'node-month', - default_cost_type: 'Supplementary', - }, - cost_type: 'Supplementary', - tiered_rates: [ - { - unit: 'USD', - value: 125.12, - usage: { - unit: 'USD', - }, - }, - ], - }, - { - description: '', - metric: { - name: 'cpu_core_request_per_hour', - metric: 'cpu_core_request_per_hour', - source_type: 'openshift container platform', - default_cost_type: 'Supplementary', - label_metric: 'CPU', - label_measurement: 'Request', - label_measurement_unit: 'core-hour', - }, - cost_type: 'Infrastructure', - tiered_rates: [ - { - unit: 'USD', - value: 5.5, - usage: { - unit: 'USD', - }, - }, - ], - }, - { - description: '', - metric: { - name: 'cpu_core_request_per_hour', - metric: 'cpu_core_request_per_hour', - source_type: 'openshift container platform', - default_cost_type: 'Supplementary', - label_metric: 'CPU', - label_measurement: 'Usage', - label_measurement_unit: 'core-hour', - }, - cost_type: 'Infrastructure', - tiered_rates: [ - { - unit: 'USD', - value: 7.2, - usage: { - unit: 'USD', - }, - }, - ], - }, - { - description: '', - metric: { - name: 'cpu_core_request_per_hour', - metric: 'cpu_core_request_per_hour', - source_type: 'openshift container platform', - default_cost_type: 'Supplementary', - label_metric: 'CPU', - label_measurement: 'Request', - label_measurement_unit: 'core-hour', - }, - cost_type: 'Supplementary', - tiered_rates: [ - { - unit: 'USD', - value: 124.6, - usage: { - unit: 'USD', - }, - }, - ], - }, - ]; - render(); - const metrics = screen.getAllByRole('cell', { name: /"value":"(CPU|Node)"/ }); - expect(metrics).toMatchSnapshot(); - userEvent.click(screen.getByRole('button', { name: /metric/i })).then(() => { - expect(metrics).toMatchSnapshot(); - }); - userEvent.click(screen.getByRole('button', { name: /measurement/i })).then(() => { - expect(metrics).toMatchSnapshot(); - }); - }); -}); diff --git a/src/routes/costModels/components/rateTable.tsx b/src/routes/costModels/components/rateTable.tsx deleted file mode 100644 index f922eb295..000000000 --- a/src/routes/costModels/components/rateTable.tsx +++ /dev/null @@ -1,205 +0,0 @@ -import type { IActions, ThProps } from '@patternfly/react-table'; -import { - ActionsColumn, - ExpandableRowContent, - TableComposable, - TableVariant, - Tbody, - Td, - Th, - Thead, - Tr, -} from '@patternfly/react-table'; -import type { Rate } from 'api/rates'; -import { intl as defaultIntl } from 'components/i18n'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { formatCurrencyRate, unitsLookupKey } from 'utils/format'; - -interface RateTableProps extends WrappedComponentProps { - actions?: IActions; - isCompact?: boolean; - tiers: Rate[]; - sortCallback?: ({ index, direction }) => void; -} - -// defaultIntl required for testing -const RateTableBase: React.FC = ({ - actions = [], - intl = defaultIntl, - tiers, - sortCallback = () => {}, -}) => { - const [activeSortIndex, setActiveSortIndex] = React.useState(null); - const [activeSortDirection, setActiveSortDirection] = React.useState<'asc' | 'desc' | null>(null); - const [expanded, setExpanded] = React.useState([]); - - const getMetric = value => intl.formatMessage(messages.metricValues, { value }) || value; - const getMeasurement = (measurement, units) => { - units = intl.formatMessage(messages.units, { units: unitsLookupKey(units) }) || units; - return intl.formatMessage(messages.measurementValues, { - value: measurement.toLowerCase().replace('-', '_'), - units, - count: 2, - }); - }; - - const rows = tiers.reduce((acc, tier, ix) => { - const isTagRates = !tier.tiered_rates; - const tierRate = tier.tiered_rates ? tier.tiered_rates[0].value : 0; - return [ - ...acc, - { - data: { index: ix, hasChildren: isTagRates, tag_rates: tier.tag_rates, stateIndex: tier.stateIndex }, - cells: [ - tier.description || '', - getMetric(tier.metric.label_metric), - getMeasurement(tier.metric.label_measurement, tier.metric.label_measurement_unit), - tier.cost_type, - { - title: isTagRates - ? intl.formatMessage(messages.various) - : formatCurrencyRate(tierRate, tier.tiered_rates[0].unit), - expandToggle: isTagRates, - }, - ], - }, - ]; - }, []); - const columns = [ - { title: intl.formatMessage(messages.description) }, - { title: intl.formatMessage(messages.metric), sortable: true, sortIndex: 1 }, - { title: intl.formatMessage(messages.measurement), sortable: true, sortIndex: 2 }, - { title: intl.formatMessage(messages.calculationType) }, - { title: intl.formatMessage(messages.rate) }, - ]; - const tagColumns = [ - intl.formatMessage(messages.costModelsTagRateTableKey), - intl.formatMessage(messages.costModelsTagRateTableValue), - intl.formatMessage(messages.rate), - intl.formatMessage(messages.description), - intl.formatMessage(messages.default), - ]; - - const getSortParams = (columnIndex: number): ThProps['sort'] => ({ - sortBy: { - index: activeSortIndex, - direction: activeSortDirection, - defaultDirection: 'asc', - }, - onSort: (_event, index, direction) => { - setActiveSortIndex(index); - setActiveSortDirection(direction); - sortCallback({ index, direction }); - }, - columnIndex, - }); - const setRowExpanded = rowIndex => { - expanded.includes(rowIndex) - ? setExpanded(expanded.filter(ex => ex !== rowIndex)) - : setExpanded(expanded.concat([rowIndex])); - }; - const compoundExpandParams = rowIndex => ({ - isExpanded: expanded.includes(rowIndex), - onToggle: () => setRowExpanded(rowIndex), - expandId: 'expand-' + rowIndex, - rowIndex, - columnIndex: 4, - }); - - const sortedRows = - activeSortIndex === null - ? rows - : rows.sort((a, b) => { - const aValue = a.cells[activeSortIndex]; - const bValue = b.cells[activeSortIndex]; - if (activeSortDirection === 'asc') { - return (aValue as string).localeCompare(bValue as string); - } - return (bValue as string).localeCompare(aValue as string); - }); - - return ( - - - - {columns.map((col: { title?: string; sortable?: boolean }, i) => ( - - {col.title} - - ))} - {!!actions.length && } - - - {sortedRows.map((row, rowIndex) => { - const isExpanded = row.data.hasChildren && expanded.includes(rowIndex); - return ( - - - {row.cells.map((cell, i) => ( - - {cell.title ? cell.title : cell} - - ))} - {!!actions.length && ( - - { - return { - ...a, - onClick: () => { - a.onClick(null, rowIndex, row, null); - }, - }; - })} - /> - - )} - - {row.data.hasChildren && isExpanded && ( - - - - - - - {tagColumns.map((tag, tagIndex) => ( - {tag} - ))} - - - - {row.data.tag_rates.tag_values.map((v, index) => ( - - {index === 0 ? row.data.tag_rates.tag_key : ''} - {v.tag_value} - {formatCurrencyRate(v.value, v.unit)} - {v.description} - {v.default ? intl.formatMessage(messages.yes) : intl.formatMessage(messages.no)} - - ))} - - - - - - )} - - ); - })} - - ); -}; - -const RateTable = injectIntl(RateTableBase); -export { RateTable }; diff --git a/src/routes/costModels/components/readOnlyTooltip.test.tsx b/src/routes/costModels/components/readOnlyTooltip.test.tsx deleted file mode 100644 index c455f5d4b..000000000 --- a/src/routes/costModels/components/readOnlyTooltip.test.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import React from 'react'; - -import { ReadOnlyTooltip } from './readOnlyTooltip'; - -test('read only tooltip is disabled', () => { - render( - - - - ); - // eslint-disable-next-line testing-library/no-node-access - expect(screen.getByRole('button').closest('div').getAttribute('aria-label')).toBe('Read only'); -}); - -test('read only tooltip is enabled', () => { - render( - - - - ); - // eslint-disable-next-line testing-library/prefer-presence-queries, testing-library/no-node-access - expect(screen.getByRole('button').closest('div').getAttribute('aria-label')).toBeNull(); -}); diff --git a/src/routes/costModels/components/readOnlyTooltip.tsx b/src/routes/costModels/components/readOnlyTooltip.tsx deleted file mode 100644 index 25a82f7ca..000000000 --- a/src/routes/costModels/components/readOnlyTooltip.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Tooltip } from '@patternfly/react-core'; -import React from 'react'; - -interface ReadOnlyTooltipBase { - tooltip?: string; - children: JSX.Element; - isDisabled: boolean; -} - -export const ReadOnlyTooltip: React.FC = ({ - children, - tooltip = 'You have read only permissions', - isDisabled, -}) => { - return isDisabled ? ( - {tooltip}}> -
{children}
-
- ) : ( - children - ); -}; diff --git a/src/routes/costModels/components/toolbar/checkboxSelector.test.tsx b/src/routes/costModels/components/toolbar/checkboxSelector.test.tsx deleted file mode 100644 index 6c6734e8e..000000000 --- a/src/routes/costModels/components/toolbar/checkboxSelector.test.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; - -import { CheckboxSelector } from './checkboxSelector'; - -test('checkbox selector', async () => { - const setSelections = jest.fn(); - const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); - render( - - ); - expect(screen.queryAllByText('Resources').length).toBe(1); - expect(screen.queryAllByText('CPU').length).toBe(0); - expect(screen.queryAllByText('Memory').length).toBe(0); - expect(screen.queryAllByText('Storage').length).toBe(0); - await user.click(screen.getByRole('button')); - expect(screen.queryAllByText('CPU').length).toBe(1); - expect(screen.queryAllByText('Memory').length).toBe(1); - expect(screen.queryAllByText('Storage').length).toBe(1); - expect(setSelections.mock.calls.length).toBe(0); - await user.click(screen.getAllByRole('checkbox')[0]); - expect(setSelections.mock.calls).toEqual([['cpu']]); -}); diff --git a/src/routes/costModels/components/toolbar/checkboxSelector.tsx b/src/routes/costModels/components/toolbar/checkboxSelector.tsx deleted file mode 100644 index 4518c2e48..000000000 --- a/src/routes/costModels/components/toolbar/checkboxSelector.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { Select, SelectOption, SelectVariant } from '@patternfly/react-core'; -import React from 'react'; -import { WithStateMachine } from 'routes/costModels/components/hoc/withStateMachine'; -import { selectMachineState } from 'routes/costModels/components/logic/selectStateMachine'; -import type { Option } from 'routes/costModels/components/logic/types'; - -interface CheckboxSelectorProps { - setSelections: (selection: string) => void; - selections: string[]; - placeholderText: string; - options: Option[]; - isDisabled?: boolean; -} - -export const CheckboxSelector: React.FC = ({ - options, - placeholderText, - setSelections, - selections, - isDisabled, -}) => { - return ( - { - setSelections(evt.selection); - }, - }, - })} - > - {({ send, current }) => { - return ( - - ); - }} - - ); -}; diff --git a/src/routes/costModels/components/toolbar/primarySelector.test.tsx b/src/routes/costModels/components/toolbar/primarySelector.test.tsx deleted file mode 100644 index b8de0d2bc..000000000 --- a/src/routes/costModels/components/toolbar/primarySelector.test.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; - -import { PrimarySelector } from './primarySelector'; - -test('primary selector', async () => { - const setPrimary = jest.fn(); - const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); - render( - - ); - expect(screen.queryAllByText('Metrics').length).toBe(1); - expect(screen.queryAllByText('Measurements').length).toBe(0); - const button = screen.getByRole('button'); - await user.click(button); - const options = screen.getAllByRole('option'); - expect(options.length).toBe(2); - await user.click(options[1]); - expect(setPrimary.mock.calls).toEqual([['measurements']]); -}); diff --git a/src/routes/costModels/components/toolbar/primarySelector.tsx b/src/routes/costModels/components/toolbar/primarySelector.tsx deleted file mode 100644 index a9485bfb0..000000000 --- a/src/routes/costModels/components/toolbar/primarySelector.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Select, SelectOption } from '@patternfly/react-core'; -import { FilterIcon } from '@patternfly/react-icons/dist/esm/icons/filter-icon'; -import React from 'react'; -import { WithStateMachine } from 'routes/costModels/components/hoc/withStateMachine'; -import { selectMachineState } from 'routes/costModels/components/logic/selectStateMachine'; -import type { Option } from 'routes/costModels/components/logic/types'; - -export interface PrimarySelectorProps { - setPrimary: (primary: string) => void; - primary: string; - options: Option[]; - isDisabled?: boolean; -} - -export const PrimarySelector: React.FC = ({ setPrimary, primary, options, isDisabled }) => { - return ( - { - setPrimary(evt.selection); - }, - }, - })} - > - {({ current, send }) => { - return ( - - ); - }} - - ); -}; diff --git a/src/routes/costModels/components/warningIcon.test.tsx b/src/routes/costModels/components/warningIcon.test.tsx deleted file mode 100644 index dfb5234e5..000000000 --- a/src/routes/costModels/components/warningIcon.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import React from 'react'; - -import { WarningIcon } from './warningIcon'; - -test('warning icon', () => { - render(); - expect(screen.getByRole('img', { hidden: true })).toMatchSnapshot(); -}); diff --git a/src/routes/costModels/components/warningIcon.tsx b/src/routes/costModels/components/warningIcon.tsx deleted file mode 100644 index 5ef1a315d..000000000 --- a/src/routes/costModels/components/warningIcon.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { Tooltip } from '@patternfly/react-core'; -import { ExclamationTriangleIcon } from '@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon'; -import React from 'react'; - -interface WarningIconProps { - text: string; -} - -export const WarningIcon: React.FC = ({ text }) => { - return ( - - - - ); -}; diff --git a/src/routes/costModels/costModel/__snapshots__/dialog.test.tsx.snap b/src/routes/costModels/costModel/__snapshots__/dialog.test.tsx.snap deleted file mode 100644 index 8b19b8da9..000000000 --- a/src/routes/costModels/costModel/__snapshots__/dialog.test.tsx.snap +++ /dev/null @@ -1,26 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`dialog title renders correctly with icon and title text 1`] = ` -

- - - Test dialog -

-`; diff --git a/src/routes/costModels/costModel/addRateModal.styles.ts b/src/routes/costModels/costModel/addRateModal.styles.ts deleted file mode 100644 index 4206f289c..000000000 --- a/src/routes/costModels/costModel/addRateModal.styles.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type React from 'react'; - -export const styles = { - form: { - width: '350px', - }, -} as { [className: string]: React.CSSProperties }; diff --git a/src/routes/costModels/costModel/addRateModal.tsx b/src/routes/costModels/costModel/addRateModal.tsx deleted file mode 100644 index d15d639ba..000000000 --- a/src/routes/costModels/costModel/addRateModal.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { Alert, Button, ButtonVariant, Form, Modal } from '@patternfly/react-core'; -import type { CostModelRequest } from 'api/costModels'; -import type { CostModel } from 'api/costModels'; -import type { MetricHash } from 'api/metrics'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; -import { - canSubmit as isReadyForSubmit, - mergeToRequest, - RateForm, - useRateData, -} from 'routes/costModels/components/rateForm'; -import { initialRateFormData } from 'routes/costModels/components/rateForm/utils'; -import { createMapStateToProps } from 'store/common'; -import { costModelsActions, costModelsSelectors } from 'store/costModels'; -import { metricsSelectors } from 'store/metrics'; - -interface AddRateModalOwnProps extends WrappedComponentProps { - // TBD... -} - -interface AddRateModalStateProps { - costModel?: CostModel; - isOpen?: boolean; - isProcessing?: boolean; - metricsHash?: MetricHash; - updateError?: string; -} - -interface AddRateModalDispatchProps { - onClose?: () => void; - updateCostModel?: (uuid: string, request: CostModelRequest) => void; -} - -type AddRateModalProps = AddRateModalOwnProps & AddRateModalStateProps & AddRateModalDispatchProps; - -export const AddRateModalBase: React.FC = ({ - costModel, - intl, - isOpen, - isProcessing, - metricsHash, - onClose, - updateCostModel, - updateError, -}) => { - const rateFormData: any = useRateData(metricsHash); - const canSubmit = React.useMemo(() => isReadyForSubmit(rateFormData), [rateFormData.errors, rateFormData.rateKind]); - const onProceed = () => { - const costModelReq = mergeToRequest(metricsHash, costModel, rateFormData); - updateCostModel(costModel.uuid, costModelReq); - }; - - React.useEffect(() => { - rateFormData.reset({ ...initialRateFormData, otherTiers: costModel.rates }); - }, [isOpen]); - - return ( - - {intl.formatMessage(messages.priceListAddRate)} - , - , - ]} - > -
- {updateError && } - - -
- ); -}; - -const mapStateToProps = createMapStateToProps(state => { - const costModels = costModelsSelectors.costModels(state); - let costModel = null; - if (costModels.length > 0) { - costModel = costModels[0]; - } - return { - costModel, - isOpen: (costModelsSelectors.isDialogOpen(state)('rate') as any).addRate, - updateError: costModelsSelectors.updateError(state), - isProcessing: costModelsSelectors.updateProcessing(state), - metricsHash: metricsSelectors.metrics(state), - }; -}); - -const mapDispatchToProps = (dispatch): AddRateModalDispatchProps => { - return { - onClose: () => { - dispatch( - costModelsActions.setCostModelDialog({ - name: 'addRate', - isOpen: false, - }) - ); - }, - updateCostModel: (uuid: string, request: CostModelRequest) => - costModelsActions.updateCostModel(uuid, request, 'addRate')(dispatch), - }; -}; - -const AddRateModal = injectIntl(connect(mapStateToProps, mapDispatchToProps)(AddRateModalBase)); - -export default AddRateModal; diff --git a/src/routes/costModels/costModel/addSourceStep.tsx b/src/routes/costModels/costModel/addSourceStep.tsx deleted file mode 100644 index 16639064d..000000000 --- a/src/routes/costModels/costModel/addSourceStep.tsx +++ /dev/null @@ -1,276 +0,0 @@ -import { Pagination, Toolbar, ToolbarContent, ToolbarItem } from '@patternfly/react-core'; -import { TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; -import type { CostModel } from 'api/costModels'; -import type { Provider } from 'api/providers'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; -import { EmptyFilterState } from 'routes/components/state/emptyFilterState'; -import { LoadingState } from 'routes/components/state/loadingState'; -import { SourcesModalErrorState } from 'routes/costModels/components/errorState'; -import { addMultiValueQuery, removeMultiValueQuery } from 'routes/costModels/components/filterLogic'; -import { WarningIcon } from 'routes/costModels/components/warningIcon'; -import { createMapStateToProps } from 'store/common'; -import { sourcesActions, sourcesSelectors } from 'store/sourceSettings'; - -import { AssignSourcesToolbar } from './assignSourcesModalToolbar'; - -interface AddSourcesStepOwnProps extends WrappedComponentProps { - checked: { [uuid: string]: { selected: boolean; meta: Provider } }; - costModel: CostModel; - fetch: typeof sourcesActions.fetchSources; - fetchingSourcesError: string; - isLoadingSources: boolean; - providers: Provider[]; - pagination: { page: number; perPage: number; count: number }; - query: { name: string; type: string; offset: string; limit: string }; - setState: (newState: { [uuid: string]: { selected: boolean; meta: Provider } }) => void; -} - -interface AddSourcesStepStateProps { - currentFilter: { - name: string; - value: string; - }; - filter: string; -} - -interface AddSourcesStepDispatchProps { - updateFilter: typeof sourcesActions.updateFilterToolbar; -} - -interface AddSourcesStepState { - // TBD... -} - -type AddSourcesStepProps = AddSourcesStepOwnProps & AddSourcesStepStateProps & AddSourcesStepDispatchProps; - -class AddSourcesStepBase extends React.Component { - public render() { - const { costModel, intl } = this.props; - - if (this.props.isLoadingSources) { - return ; - } - if (this.props.fetchingSourcesError) { - return ; - } - - const onSelect = (_evt, isSelected, rowId) => { - if (rowId === -1) { - const pageSelections = this.props.providers.reduce((acc, cur) => { - // If assigned to another cost model, maintain original selection - const isAssigned = - cur.cost_models.length && cur.cost_models.find(cm => cm.name === costModel.name) === undefined; - const selected = this.props.checked[cur.uuid] ? this.props.checked[cur.uuid].selected : false; - return { - ...acc, - [cur.uuid]: { selected: isAssigned ? selected : isSelected, meta: cur, isAssigned }, - }; - }, {}); - const newState = { - ...this.props.checked, - ...pageSelections, - }; - this.props.setState( - newState as { - [uuid: string]: { selected: boolean; meta: Provider }; - } - ); - return; - } - this.props.setState({ - ...this.props.checked, - [this.props.providers[rowId].uuid]: { - selected: isSelected, - meta: this.props.providers[rowId], - }, - }); - }; - - const sources = this.props.providers.map(providerData => { - const isSelected = this.props.checked[providerData.uuid] ? this.props.checked[providerData.uuid].selected : false; - const provCostModels = - providerData.cost_models === undefined - ? intl.formatMessage(messages.costModelsWizardSourceTableDefaultCostModel) - : providerData.cost_models.map(cm => cm.name).join(','); - const isAssigned = - providerData.cost_models.length && - providerData.cost_models.find(cm => cm.name === costModel.name) === undefined; - // If assigned to another cost model, show warning - const warningIcon = isAssigned ? ( - - ) : null; - const cellName = ( -
- {providerData.name} {warningIcon} -
- ); - return { - cells: [cellName, provCostModels || ''], - selected: isSelected, - disableSelection: isAssigned, - }; - }); - - const sourceTypeMap = { - 'OpenShift Container Platform': 'OCP', - 'Microsoft Azure': 'Azure', - 'Amazon Web Services': 'AWS', - }; - - const source_type = sourceTypeMap[costModel.source_type]; - return ( - <> - this.props.fetch(`source_type=${source_type}&limit=${this.props.pagination.perPage}`), - onRemove: (category, chip) => { - const newQuery = removeMultiValueQuery({ - name: this.props.query.name ? this.props.query.name.split(',') : [], - })(category, chip); - this.props.fetch( - `source_type=${source_type}${newQuery.name ? `&name=${newQuery.name.join(',')}` : ''}&offset=0&limit=${ - this.props.pagination.perPage - }` - ); - }, - query: { - name: this.props.query.name ? this.props.query.name.split(',') : [], - }, - }} - filterInputProps={{ - id: 'assign-sources-modal-toolbar-input', - onChange: value => - this.props.updateFilter({ - currentFilterType: 'name', - currentFilterValue: value, - }), - value: this.props.currentFilter.value, - onSearch: () => { - const curQuery = this.props.query.name ? this.props.query.name.split(',') : []; - const newQuery = addMultiValueQuery({ name: curQuery })('name', this.props.currentFilter.value); - this.props.fetch( - `source_type=${source_type}&name=${newQuery.name.join(',')}&limit=${ - this.props.pagination.perPage - }&offset=0` - ); - }, - }} - paginationProps={{ - isCompact: true, - itemCount: this.props.pagination.count, - perPage: this.props.pagination.perPage, - page: this.props.pagination.page, - onPerPageSelect: (_evt, newPerPage) => { - this.props.fetch( - `source_type=${source_type}&limit=${newPerPage}&offset=0&${ - this.props.query.name ? `name=${this.props.query.name}` : '' - }` - ); - }, - onSetPage: (_evt, newPage) => { - this.props.fetch( - `source_type=${source_type}&limit=${this.props.pagination.perPage}&offset=${ - this.props.pagination.perPage * (newPage - 1) - }&${this.props.query.name ? `name=${this.props.query.name}` : ''}` - ); - }, - }} - /> - {sources.length > 0 && ( - - - - onSelect(_evt, isSelecting, -1), - isSelected: sources.filter(s => s.disableSelection || s.selected).length === sources.length, - }} - > - {intl.formatMessage(messages.names, { count: 1 })} - {intl.formatMessage(messages.costModelsWizardSourceTableCostModel)} - - - - {sources.map((s, rowIndex) => ( - - onSelect(_evt, !s.selected, rowIndex), - isSelected: s.selected, - rowIndex, - }} - > - {s.cells.map((c, cellIndex) => ( - {c} - ))} - - ))} - - - )} - {sources.length === 0 && ( - - )} - - - - { - this.props.fetch( - `limit=${newPerPage}&offset=0&${this.props.query.name ? `name=${this.props.query.name}` : ''}` - ); - }} - onSetPage={(_evt, newPage) => { - this.props.fetch( - `limit=${this.props.pagination.perPage}&offset=${this.props.pagination.perPage * (newPage - 1)}&${ - this.props.query.name ? `name=${this.props.query.name}` : '' - }` - ); - }} - /> - - - - - ); - } -} - -const mapStateToProps = createMapStateToProps(state => { - return { - currentFilter: { - name: sourcesSelectors.currentFilterType(state), - value: sourcesSelectors.currentFilterValue(state), - }, - filter: sourcesSelectors.filter(state), - }; -}); - -const mapDispatchToProps: AddSourcesStepDispatchProps = { - updateFilter: sourcesActions.updateFilterToolbar, -}; - -const AddSourcesStep = injectIntl(connect(mapStateToProps, mapDispatchToProps)(AddSourcesStepBase)); - -export default AddSourcesStep; diff --git a/src/routes/costModels/costModel/addSourceWizard.tsx b/src/routes/costModels/costModel/addSourceWizard.tsx deleted file mode 100644 index 07e0ca088..000000000 --- a/src/routes/costModels/costModel/addSourceWizard.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import { - Alert, - Button, - Grid, - GridItem, - Modal, - Stack, - StackItem, - Text, - TextContent, - TextVariants, -} from '@patternfly/react-core'; -import type { CostModel } from 'api/costModels'; -import type { Provider } from 'api/providers'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; -import { parseApiError } from 'routes/costModels/costModelWizard/parseError'; -import { FetchStatus } from 'store/common'; -import { createMapStateToProps } from 'store/common'; -import { costModelsSelectors } from 'store/costModels'; -import { sourcesActions, sourcesSelectors } from 'store/sourceSettings'; -import type { RouterComponentProps } from 'utils/router'; -import { withRouter } from 'utils/router'; - -import AddSourceStep from './addSourceStep'; - -interface AddSourceWizardOwnProps extends RouterComponentProps { - assigned?: Provider[]; - costModel?: CostModel; - isOpen?: boolean; - onClose?: () => void; - onSave?: (sources_uuid: string[]) => void; -} - -interface AddSourceWizardStateProps { - fetchingSourcesError?: string; - pagination?: { page: number; perPage: number; count: number }; - isLoadingSources?: boolean; - isUpdateInProgress?: boolean; - providers?: Provider[]; - query?: any; - updateApiError?: string; -} - -interface AddSourceWizardDispatchProps { - fetch: typeof sourcesActions.fetchSources; -} - -interface AddSourcesStepState { - checked: { [uuid: string]: { disabled?: boolean; selected: boolean; meta: Provider } }; -} - -type AddSourceWizardProps = AddSourceWizardOwnProps & - AddSourceWizardStateProps & - AddSourceWizardDispatchProps & - WrappedComponentProps; - -const sourceTypeMap = { - 'OpenShift Container Platform': 'OCP', - 'Microsoft Azure': 'Azure', - 'Amazon Web Services': 'AWS', -}; - -class AddSourceWizardBase extends React.Component { - protected defaultState: AddSourcesStepState = { - checked: {}, - }; - public state: AddSourcesStepState = { ...this.defaultState }; - - public componentDidMount() { - const { assigned } = this.props; - - const { - costModel: { source_type }, - fetch, - } = this.props; - const sourceType = sourceTypeMap[source_type]; - fetch(`type=${sourceType}&limit=10&offset=0`); - - const checked = {}; - for (const cur of assigned) { - checked[cur.uuid] = { selected: true, meta: cur, disabled: false }; - } - this.setState({ checked }); - } - - private hasSelections = () => { - const { checked } = this.state; - let result = false; - - for (const item of Object.keys(checked)) { - if (checked[item].selected && !checked[item].disabled) { - result = true; - break; - } - } - return result; - }; - - public render() { - const { intl, isUpdateInProgress, onClose, isOpen, onSave, costModel, updateApiError } = this.props; - - return ( - { - onSave(Object.keys(this.state.checked).filter(uuid => this.state.checked[uuid].selected)); - }} - > - {intl.formatMessage(messages.costModelsAssignSourcesParen)} - , - , - ]} - > - - {updateApiError && } - - - - - {intl.formatMessage(messages.names, { count: 1 })} - - - - - {this.props.costModel.name} - - - - - {intl.formatMessage(messages.sourceType)} - - - - - {this.props.costModel.source_type} - - - - - - { - this.setState({ checked: newState }); - }} - /> - - - - ); - } -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const mapStateToProps = createMapStateToProps(state => { - return { - fetchingSourcesError: sourcesSelectors.error(state) ? parseApiError(sourcesSelectors.error(state)) : null, - isLoadingSources: sourcesSelectors.status(state) === FetchStatus.inProgress, - isUpdateInProgress: costModelsSelectors.updateProcessing(state), - pagination: sourcesSelectors.pagination(state), - providers: sourcesSelectors.sources(state), - query: sourcesSelectors.query(state), - updateApiError: costModelsSelectors.updateError(state), - }; -}); - -const mapDispatchToProps: AddSourceWizardDispatchProps = { - fetch: sourcesActions.fetchSources, -}; - -const AddSourceWizard = injectIntl(withRouter(connect(mapStateToProps, mapDispatchToProps)(AddSourceWizardBase))); - -export default AddSourceWizard; diff --git a/src/routes/costModels/costModel/assignSourcesModalToolbar.tsx b/src/routes/costModels/costModel/assignSourcesModalToolbar.tsx deleted file mode 100644 index 887c2e78e..000000000 --- a/src/routes/costModels/costModel/assignSourcesModalToolbar.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import type { PaginationProps } from '@patternfly/react-core'; -import { - InputGroup, - InputGroupText, - Pagination, - TextInput, - Toolbar, - ToolbarContent, - ToolbarFilter, - ToolbarItem, - ToolbarToggleGroup, -} from '@patternfly/react-core'; -import { FilterIcon } from '@patternfly/react-icons/dist/esm/icons/filter-icon'; -import { SearchIcon } from '@patternfly/react-icons/dist/esm/icons/search-icon'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; - -interface FilterInputProps { - id: string; - value: string; - onChange: (value: string, event: React.FormEvent) => void; - onSearch: (evt: React.KeyboardEvent) => void; - placeholder?: string; -} - -const FilterInput: React.FC = ({ id, placeholder = '', value, onChange, onSearch }) => { - return ( - - ) => { - if (evt.key !== 'Enter' || value === '') { - return; - } - onSearch(evt); - }} - /> - - - - - ); -}; - -interface AssignSourcesToolbarBaseProps extends WrappedComponentProps { - paginationProps: PaginationProps; - filterInputProps: Omit; - filter: { - onRemove: (category: string, chip: string) => void; - onClearAll: () => void; - query: { name?: string[] }; - }; -} - -export const AssignSourcesToolbarBase: React.FC = ({ - filterInputProps, - intl, - paginationProps, - filter, -}) => { - return ( - - - }> - - - - - - - - - - - - ); -}; - -export const AssignSourcesToolbar = injectIntl(AssignSourcesToolbarBase); diff --git a/src/routes/costModels/costModel/costCalc.styles.ts b/src/routes/costModels/costModel/costCalc.styles.ts deleted file mode 100644 index 16a1c9aa1..000000000 --- a/src/routes/costModels/costModel/costCalc.styles.ts +++ /dev/null @@ -1,38 +0,0 @@ -import global_FontSize_md from '@patternfly/react-tokens/dist/js/global_FontSize_md'; -import global_FontSize_xl from '@patternfly/react-tokens/dist/js/global_FontSize_xl'; -import type React from 'react'; - -export const styles = { - card: { - minHeight: 250, - }, - cardDescription: { - fontSize: global_FontSize_md.value, - }, - cardBody: { - fontSize: global_FontSize_xl.value, - textAlign: 'center', - }, - exampleMargin: { - marginLeft: 30, - }, - inputField: { - borderLeft: 0, - width: 175, - }, - markupRadio: { - marginBottom: 6, - }, - markupRadioContainer: { - marginTop: 6, - }, - rateContainer: { - marginLeft: 20, - }, - percent: { - borderLeft: 0, - }, - sign: { - borderRight: 0, - }, -} as { [className: string]: React.CSSProperties }; diff --git a/src/routes/costModels/costModel/costModelInfo.styles.ts b/src/routes/costModels/costModel/costModelInfo.styles.ts deleted file mode 100644 index fbc86cd2c..000000000 --- a/src/routes/costModels/costModel/costModelInfo.styles.ts +++ /dev/null @@ -1,35 +0,0 @@ -import global_BackgroundColor_light_100 from '@patternfly/react-tokens/dist/js/global_BackgroundColor_light_100'; -import global_spacer_lg from '@patternfly/react-tokens/dist/js/global_spacer_lg'; -import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; -import global_spacer_sm from '@patternfly/react-tokens/dist/js/global_spacer_sm'; -import type React from 'react'; - -export const styles = { - headerDescription: { - width: '97%', - wordWrap: 'break-word', - }, - content: { - paddingTop: global_spacer_lg.value, - height: '182vh', - }, - costCalculation: { - marginLeft: global_spacer_lg.value, - marginRight: global_spacer_lg.value, - }, - costmodelsContainer: { - marginLeft: global_spacer_lg.value, - marginRight: global_spacer_lg.value, - backgroundColor: global_BackgroundColor_light_100.value, - paddingBottom: global_spacer_md.value, - paddingTop: global_spacer_md.value, - }, - headerCostModel: { - padding: global_spacer_lg.var, - paddingBottom: 0, - backgroundColor: global_BackgroundColor_light_100.value, - }, - title: { - paddingBottom: global_spacer_sm.var, - }, -} as { [className: string]: React.CSSProperties }; diff --git a/src/routes/costModels/costModel/costModelInfo.tsx b/src/routes/costModels/costModel/costModelInfo.tsx deleted file mode 100644 index 3f511d4f9..000000000 --- a/src/routes/costModels/costModel/costModelInfo.tsx +++ /dev/null @@ -1,199 +0,0 @@ -import { - EmptyState, - EmptyStateBody, - EmptyStateIcon, - Grid, - GridItem, - PageSection, - TabContent, - Title, - TitleSizes, -} from '@patternfly/react-core'; -import { ErrorCircleOIcon } from '@patternfly/react-icons/dist/esm/icons/error-circle-o-icon'; -import { PageHeader, PageHeaderTitle } from '@redhat-cloud-services/frontend-components/PageHeader'; -import type { CostModel } from 'api/costModels'; -import type { AxiosError } from 'axios'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; -import { Loading } from 'routes/components/page/loading'; -import { NotAvailable } from 'routes/components/page/notAvailable'; -import DistributionCard from 'routes/costModels/costModel/distribution'; -import MarkupCard from 'routes/costModels/costModel/markup'; -import PriceListTable from 'routes/costModels/costModel/priceListTable'; -import SourceTable from 'routes/costModels/costModel/sourceTable'; -import { parseApiError } from 'routes/costModels/costModelWizard/parseError'; -import { createMapStateToProps, FetchStatus } from 'store/common'; -import { costModelsActions, costModelsSelectors } from 'store/costModels'; -import { metricsActions, metricsSelectors } from 'store/metrics'; -import { rbacActions, rbacSelectors } from 'store/rbac'; -import type { RouterComponentProps } from 'utils/router'; -import { withRouter } from 'utils/router'; - -import { styles } from './costModelInfo.styles'; -import Header from './header'; - -interface CostModelInfoOwnProps { - costModels: CostModel[]; - costModelStatus: FetchStatus; - costModelError: AxiosError; - distribution: { value: string }; - fetchMetrics: typeof metricsActions.fetchMetrics; - markup: { value: string }; - metricsStatus: FetchStatus; - metricsError: AxiosError; - fetchRbac: typeof rbacActions.fetchRbac; - rbacStatus: FetchStatus; - rbacError: Error; - fetchCostModels: typeof costModelsActions.fetchCostModels; -} - -type CostModelInfoProps = CostModelInfoOwnProps & RouterComponentProps & WrappedComponentProps; - -interface CostModelInfoState { - tabIndex: number; -} - -class CostModelInfo extends React.Component { - public tabRefs = [React.createRef(), React.createRef(), React.createRef()]; - constructor(props) { - super(props); - this.state = { tabIndex: 0 }; - } - - public componentDidMount() { - this.props.fetchRbac(); - this.props.fetchMetrics(); - this.props.fetchCostModels(`uuid=${this.props.router.params.uuid}`); - } - - public render() { - const { costModels, costModelError, costModelStatus, intl, metricsError, metricsStatus, rbacError, rbacStatus } = - this.props; - - if ( - metricsStatus !== FetchStatus.complete || - rbacStatus !== FetchStatus.complete || - costModelStatus !== FetchStatus.complete - ) { - return ; - } - - const fetchError = metricsError || rbacError || costModelError; - if (fetchError) { - if (costModelError !== null) { - const costModelErrMessage = parseApiError(costModelError); - if (costModelErrMessage === 'detail: Invalid provider uuid') { - return ( - <> - - - - - - - - {intl.formatMessage(messages.costModelsUUIDEmptyState)} - - - {intl.formatMessage(messages.costModelsUUIDEmptyStateDesc, { - uuid: this.props.router.params.uuid, - })} - - - - - ); - } - } - return ; - } - - const current = costModels[0]; - const sources = current.sources; - return ( -
-
this.setState({ tabIndex })} - /> -
- {current.source_type === 'OpenShift Container Platform' ? ( - <> - - - - - ) : ( - <> - - - - )} -
-
- ); - } -} - -export default injectIntl( - withRouter( - connect( - createMapStateToProps(store => { - return { - costModels: costModelsSelectors.costModels(store), - costModelError: costModelsSelectors.error(store), - costModelStatus: costModelsSelectors.status(store), - metricsHash: metricsSelectors.metrics(store), - maxRate: metricsSelectors.maxRate(store), - costTypes: metricsSelectors.costTypes(store), - metricsError: metricsSelectors.metricsState(store).error, - metricsStatus: metricsSelectors.status(store), - rbacError: rbacSelectors.selectRbacState(store).error, - rbacStatus: rbacSelectors.selectRbacState(store).status, - }; - }), - { - fetchMetrics: metricsActions.fetchMetrics, - fetchRbac: rbacActions.fetchRbac, - fetchCostModels: costModelsActions.fetchCostModels, - } - )(CostModelInfo) - ) -); diff --git a/src/routes/costModels/costModel/costModelsDetails.styles.ts b/src/routes/costModels/costModel/costModelsDetails.styles.ts deleted file mode 100644 index efd594557..000000000 --- a/src/routes/costModels/costModel/costModelsDetails.styles.ts +++ /dev/null @@ -1,71 +0,0 @@ -import global_BackgroundColor_light_100 from '@patternfly/react-tokens/dist/js/global_BackgroundColor_light_100'; -import global_spacer_lg from '@patternfly/react-tokens/dist/js/global_spacer_lg'; -import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; -import global_spacer_sm from '@patternfly/react-tokens/dist/js/global_spacer_sm'; -import type React from 'react'; - -export const styles = { - headerDescription: { - width: '97%', - wordWrap: 'break-word', - }, - content: { - paddingTop: global_spacer_lg.value, - height: '182vh', - }, - costmodelsContainer: { - marginLeft: global_spacer_lg.value, - marginRight: global_spacer_lg.value, - paddingBottom: global_spacer_md.value, - paddingTop: global_spacer_md.value, - paddingLeft: global_spacer_lg.value, - paddingRight: global_spacer_lg.value, - }, - currency: { - paddingBottom: global_spacer_md.value, - paddingTop: global_spacer_lg.value, - }, - tableContainer: { - marginLeft: global_spacer_lg.value, - marginRight: global_spacer_lg.value, - }, - paginationContainer: { - paddingBottom: global_spacer_md.value, - paddingTop: global_spacer_md.value, - paddingLeft: global_spacer_lg.value, - paddingRight: global_spacer_lg.value, - marginLeft: global_spacer_lg.value, - marginRight: global_spacer_lg.value, - marginBottom: global_spacer_lg.value, - backgroundColor: global_BackgroundColor_light_100.value, - }, - toolbarContainer: { - paddingBottom: global_spacer_md.value, - paddingTop: global_spacer_md.value, - marginLeft: global_spacer_lg.value, - marginRight: global_spacer_lg.value, - backgroundColor: global_BackgroundColor_light_100.value, - }, - header: { - padding: global_spacer_lg.var, - backgroundColor: global_BackgroundColor_light_100.var, - }, - headerContent: { - display: 'flex', - justifyContent: 'space-between', - }, - headerCostModel: { - padding: global_spacer_lg.value, - paddingBottom: 0, - backgroundColor: global_BackgroundColor_light_100.var, - }, - breadcrumb: { - paddingBottom: global_spacer_md.var, - }, - title: { - paddingBottom: global_spacer_sm.var, - }, - sourceTypeTitle: { - paddingBottom: global_spacer_md.var, - }, -} as { [className: string]: React.CSSProperties }; diff --git a/src/routes/costModels/costModel/dialog.test.tsx b/src/routes/costModels/costModel/dialog.test.tsx deleted file mode 100644 index c29b65c35..000000000 --- a/src/routes/costModels/costModel/dialog.test.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import React from 'react'; - -import Dialog from './dialog'; - -const defaultProps = { - onClose: jest.fn(), - onProceed: jest.fn(), - isOpen: true, - title: 'Test dialog', - body:
This is a test dialog body
, - t: (text: string) => text, -}; - -test('dialog title renders correctly with icon and title text', () => { - render(); - expect(screen.getByText('Test dialog')).toMatchSnapshot(); -}); - -test('dialog with a delete action', () => { - render(); - expect(screen.getByRole('button', { name: 'Delete!' })).not.toBeNull(); -}); - -test('dialog with no action', () => { - render(); - // should only be "x" and "close", so 2 buttons - expect(screen.getAllByRole('button').length).toBe(2); -}); - -test('dialog with error', () => { - render(); - expect(screen.getByText(/danger alert/i)).not.toBeNull(); - expect(screen.getByText(/opps!/i)).not.toBeNull(); -}); - -test('dialog is small', () => { - render(); - expect(screen.getByLabelText(/test dialog/i).getAttribute('class')).toContain('pf-m-sm'); -}); diff --git a/src/routes/costModels/costModel/dialog.tsx b/src/routes/costModels/costModel/dialog.tsx deleted file mode 100644 index 46f2fe090..000000000 --- a/src/routes/costModels/costModel/dialog.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { Alert, Button, Modal, Title, TitleSizes } from '@patternfly/react-core'; -import { ExclamationTriangleIcon } from '@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon'; -import { intl as defaultIntl } from 'components/i18n'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; - -interface Props extends WrappedComponentProps { - isSmall?: boolean; - onClose: () => void; - onProceed: () => void; - title: string; - body: React.ReactElement; - actionText?: string; - isOpen?: boolean; - isProcessing?: boolean; - error?: string; -} - -const DialogBase: React.FC = ({ - intl = defaultIntl, // Default required for testing - onClose, - onProceed, - title, - body, - actionText, - isProcessing = false, - isOpen = false, - isSmall = false, - error = '', -}) => { - const CancelButtonSecondary = ( - - ); - const ProceedButton = ( - - ); - const CloseButtonPrimary = ( - - ); - const actions = actionText !== '' ? [ProceedButton, CancelButtonSecondary] : [CloseButtonPrimary]; - return ( - - {title} - - } - isOpen={isOpen} - onClose={onClose} - actions={actions} - variant={isSmall ? 'small' : 'default'} - > - {error && } - {body} - - ); -}; - -export default injectIntl(DialogBase); diff --git a/src/routes/costModels/costModel/distribution.tsx b/src/routes/costModels/costModel/distribution.tsx deleted file mode 100644 index 2924d7f3a..000000000 --- a/src/routes/costModels/costModel/distribution.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { - Card, - CardActions, - CardBody, - CardHeader, - CardHeaderMain, - Dropdown, - DropdownItem, - DropdownPosition, - KebabToggle, - Title, - TitleSizes, -} from '@patternfly/react-core'; -import type { CostModel } from 'api/costModels'; -import messages from 'locales/messages'; -import React from 'react'; -import { useIntl } from 'react-intl'; -import { connect } from 'react-redux'; -import { ReadOnlyTooltip } from 'routes/costModels/components/readOnlyTooltip'; -import { createMapStateToProps } from 'store/common'; -import { costModelsActions, costModelsSelectors } from 'store/costModels'; -import { rbacSelectors } from 'store/rbac'; - -import { styles } from './costCalc.styles'; -import UpdateDistributionDialog from './updateDistributionDialog'; - -interface Props { - isWritePermission: boolean; - isUpdateDialogOpen: boolean; - current: CostModel; - setCostModelDialog: typeof costModelsActions.setCostModelDialog; -} - -const DistributionCardBase: React.FC = ({ - isWritePermission, - setCostModelDialog, - current, - isUpdateDialogOpen, -}) => { - const intl = useIntl(); - const [dropdownIsOpen, setDropdownIsOpen] = React.useState(false); - - return ( - <> - {isUpdateDialogOpen && } - - - - - {intl.formatMessage(messages.costDistribution)} - - - - } - isOpen={dropdownIsOpen} - onSelect={() => setDropdownIsOpen(false)} - position={DropdownPosition.right} - isPlain - dropdownItems={[ - - setCostModelDialog({ isOpen: true, name: 'updateDistribution' })} - component="button" - > - {intl.formatMessage(messages.costModelsDistributionEdit)} - - , - ]} - /> - - - {intl.formatMessage(messages.costModelsDistributionDesc)} - - -
- {intl.formatMessage(messages.distributionTypeDesc, { - type: current.distribution_info.distribution_type, - })} -
-
- {intl.formatMessage(messages.distributeCosts, { - value: current.distribution_info.platform_cost, - type: 'platform', - })} -
-
- {intl.formatMessage(messages.distributeCosts, { - value: current.distribution_info.worker_cost, - type: 'worker', - })} -
-
-
- - ); -}; - -export default connect( - createMapStateToProps(state => { - const { updateDistribution } = costModelsSelectors.isDialogOpen(state)('distribution'); - return { - isUpdateDialogOpen: updateDistribution, - costModelDialog: costModelsSelectors.isDialogOpen(state)('distribution'), - isWritePermission: rbacSelectors.isCostModelWritePermission(state), - }; - }), - { - setCostModelDialog: costModelsActions.setCostModelDialog, - } -)(DistributionCardBase); diff --git a/src/routes/costModels/costModel/header.tsx b/src/routes/costModels/costModel/header.tsx deleted file mode 100644 index 5e1a869c3..000000000 --- a/src/routes/costModels/costModel/header.tsx +++ /dev/null @@ -1,229 +0,0 @@ -import { - Breadcrumb, - BreadcrumbItem, - Dropdown, - DropdownItem, - KebabToggle, - List, - ListItem, - Split, - SplitItem, - Tab, - Tabs, - TabTitleText, - TextContent, - TextList, - TextListItem, - TextListItemVariants, - TextListVariants, - Title, - TitleSizes, -} from '@patternfly/react-core'; -import type { CostModel } from 'api/costModels'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; -import { Link } from 'react-router-dom'; -import { routes } from 'routes'; -import { ReadOnlyTooltip } from 'routes/costModels/components/readOnlyTooltip'; -import { styles } from 'routes/costModels/costModel/costModelsDetails.styles'; -import Dialog from 'routes/costModels/costModel/dialog'; -import UpdateCostModelModal from 'routes/costModels/costModel/updateCostModel'; -import { createMapStateToProps } from 'store/common'; -import { costModelsActions, costModelsSelectors } from 'store/costModels'; -import { rbacSelectors } from 'store/rbac'; -import { formatPath } from 'utils/paths'; -import type { RouterComponentProps } from 'utils/router'; -import { withRouter } from 'utils/router'; -interface Props extends RouterComponentProps, WrappedComponentProps { - tabRefs: any[]; - tabIndex: number; - onSelectTab: (index: number) => void; - setDialogOpen: typeof costModelsActions.setCostModelDialog; - current: CostModel; - isDialogOpen: { deleteCostModel: boolean; updateCostModel: boolean; createWizard: boolean }; - isDeleteProcessing: boolean; - deleteError: string; - deleteCostModel: typeof costModelsActions.deleteCostModel; - isWritePermission: boolean; -} - -const Header: React.FC = ({ - current, - deleteCostModel, - deleteError, - intl, - isDeleteProcessing, - isDialogOpen, - isWritePermission, - onSelectTab, - router, - setDialogOpen, - tabRefs, - tabIndex, -}) => { - const [dropdownIsOpen, setDropdownIsOpen] = React.useState(false); - - return ( - <> - {isDialogOpen.updateCostModel && } - setDialogOpen({ name: 'deleteCostModel', isOpen: false })} - error={deleteError} - isProcessing={isDeleteProcessing} - onProceed={() => { - deleteCostModel(current.uuid, 'deleteCostModel', router); - }} - body={ - <> - {current.sources.length === 0 && - intl.formatMessage(messages.costModelsDeleteDesc, { - costModel: current.name, - })} - {current.sources.length > 0 && ( - <> - {intl.formatMessage(messages.costModelsDeleteSource)} -
-
- {intl.formatMessage(messages.costModelsAvailableSources)} -
- - {current.sources.map(provider => ( - {provider.name} - ))} - - - )} - - } - actionText={current.sources.length === 0 ? intl.formatMessage(messages.costModelsDelete) : ''} - /> -
-
- - ( - - {intl.formatMessage(messages.costModels)} - - )} - /> - {current.name} - -
- - - - {current.name} - - {current.description} - - - } - isOpen={dropdownIsOpen} - onSelect={() => setDropdownIsOpen(false)} - isPlain - position="right" - dropdownItems={[ - - - setDialogOpen({ - isOpen: true, - name: 'updateCostModel', - }) - } - > - {intl.formatMessage(messages.edit)} - - , - - - setDialogOpen({ - isOpen: true, - name: 'deleteCostModel', - }) - } - > - {intl.formatMessage(messages.delete)} - - , - ]} - /> - - - - - {intl.formatMessage(messages.currency)} - - {intl.formatMessage(messages.currencyOptions, { units: current.currency || 'USD' })} - - - - {current.source_type === 'OpenShift Container Platform' ? ( - onSelectTab(index)}> - {intl.formatMessage(messages.priceList)}} - tabContentId="refPriceList" - tabContentRef={tabRefs[0]} - /> - {intl.formatMessage(messages.costCalculations)}} - tabContentId="refMarkup" - tabContentRef={tabRefs[1]} - /> - {intl.formatMessage(messages.sources)}} - tabContentId="refSources" - tabContentRef={tabRefs[2]} - /> - - ) : ( - onSelectTab(index)}> - {intl.formatMessage(messages.costCalculations)}} - tabContentId="refMarkup" - tabContentRef={tabRefs[0]} - /> - {intl.formatMessage(messages.sources)}} - tabContentId="refSources" - tabContentRef={tabRefs[1]} - /> - - )} -
- - ); -}; - -export default injectIntl( - withRouter( - connect( - createMapStateToProps(state => ({ - isDialogOpen: costModelsSelectors.isDialogOpen(state)('costmodel'), - isDeleteProcessing: costModelsSelectors.deleteProcessing(state), - deleteError: costModelsSelectors.deleteError(state), - isWritePermission: rbacSelectors.isCostModelWritePermission(state), - })), - { - setDialogOpen: costModelsActions.setCostModelDialog, - deleteCostModel: costModelsActions.deleteCostModel, - } - )(Header) - ) -); diff --git a/src/routes/costModels/costModel/index.ts b/src/routes/costModels/costModel/index.ts deleted file mode 100644 index 2c4812ad8..000000000 --- a/src/routes/costModels/costModel/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './costModelInfo'; diff --git a/src/routes/costModels/costModel/markup.tsx b/src/routes/costModels/costModel/markup.tsx deleted file mode 100644 index fc59dea0f..000000000 --- a/src/routes/costModels/costModel/markup.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { - Card, - CardActions, - CardBody, - CardHeader, - CardHeaderMain, - Dropdown, - DropdownItem, - DropdownPosition, - KebabToggle, - Title, - TitleSizes, -} from '@patternfly/react-core'; -import type { CostModel } from 'api/costModels'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; -import { ReadOnlyTooltip } from 'routes/costModels/components/readOnlyTooltip'; -import { createMapStateToProps } from 'store/common'; -import { costModelsActions, costModelsSelectors } from 'store/costModels'; -import { rbacSelectors } from 'store/rbac'; -import { formatPercentageMarkup } from 'utils/format'; - -import { styles } from './costCalc.styles'; -import UpdateMarkupDialog from './updateMarkupDialog'; - -interface Props extends WrappedComponentProps { - isWritePermission: boolean; - isUpdateDialogOpen: boolean; - current: CostModel; - setCostModelDialog: typeof costModelsActions.setCostModelDialog; -} - -const MarkupCardBase: React.FC = ({ - intl, - isWritePermission, - setCostModelDialog, - current, - isUpdateDialogOpen, -}) => { - const [dropdownIsOpen, setDropdownIsOpen] = React.useState(false); - const markupValue = formatPercentageMarkup( - current && current.markup && current.markup.value ? Number(current.markup.value) : 0 - ); - - return ( - <> - {isUpdateDialogOpen && } - - - - - {intl.formatMessage(messages.markupOrDiscount)} - - - - } - isOpen={dropdownIsOpen} - onSelect={() => setDropdownIsOpen(false)} - position={DropdownPosition.right} - isPlain - dropdownItems={[ - - setCostModelDialog({ isOpen: true, name: 'updateMarkup' })} - component="button" - > - {intl.formatMessage(messages.editMarkup)} - - , - ]} - /> - - - {intl.formatMessage(messages.markupOrDiscountDesc)} - - {intl.formatMessage(messages.percent, { value: markupValue })} - - - - ); -}; - -export default injectIntl( - connect( - createMapStateToProps(state => { - const { updateMarkup } = costModelsSelectors.isDialogOpen(state)('markup'); - return { - isUpdateDialogOpen: updateMarkup, - costModelDialog: costModelsSelectors.isDialogOpen(state)('markup'), - isWritePermission: rbacSelectors.isCostModelWritePermission(state), - }; - }), - { - setCostModelDialog: costModelsActions.setCostModelDialog, - } - )(MarkupCardBase) -); diff --git a/src/routes/costModels/costModel/priceListTable.tsx b/src/routes/costModels/costModel/priceListTable.tsx deleted file mode 100644 index 53f3de499..000000000 --- a/src/routes/costModels/costModel/priceListTable.tsx +++ /dev/null @@ -1,413 +0,0 @@ -import { - Bullseye, - Button, - EmptyState, - EmptyStateBody, - EmptyStateIcon, - List, - ListItem, - Pagination, - Title, - TitleSizes, - Toolbar, - ToolbarContent, - ToolbarItem, - ToolbarItemVariant, -} from '@patternfly/react-core'; -import { PlusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/plus-circle-icon'; -import { SortByDirection } from '@patternfly/react-table'; -import { Unavailable } from '@redhat-cloud-services/frontend-components/Unavailable'; -import type { CostModel } from 'api/costModels'; -import type { MetricHash } from 'api/metrics'; -import type { Rate } from 'api/rates'; -import type { AxiosError } from 'axios'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; -import { EmptyFilterState } from 'routes/components/state/emptyFilterState'; -import { LoadingState } from 'routes/components/state/loadingState'; -import { WithPriceListSearch } from 'routes/costModels/components/hoc/withPriceListSearch'; -import { PriceListToolbar } from 'routes/costModels/components/priceListToolbar'; -import { compareBy } from 'routes/costModels/components/rateForm/utils'; -import { RateTable } from 'routes/costModels/components/rateTable'; -import { CheckboxSelector } from 'routes/costModels/components/toolbar/checkboxSelector'; -import { PrimarySelector } from 'routes/costModels/components/toolbar/primarySelector'; -import { FetchStatus } from 'store/common'; -import { createMapStateToProps } from 'store/common'; -import { costModelsActions, costModelsSelectors } from 'store/costModels'; -import { metricsSelectors } from 'store/metrics'; -import { rbacSelectors } from 'store/rbac'; -import { unitsLookupKey } from 'utils/format'; - -import AddRateModal from './addRateModal'; -import Dialog from './dialog'; -import UpdateRateModal from './updateRateModel'; - -interface PriceListTableProps extends WrappedComponentProps { - assignees?: string[]; - costModel?: string; - current: CostModel; - costTypes: string[]; - error: string; - isDialogOpen: { deleteRate: boolean; updateRate: boolean; addRate: boolean }; - isLoading: boolean; - isWritePermission: boolean; - fetchError: AxiosError; - fetchStatus: FetchStatus; - metricsHash: MetricHash; - updateCostModel: typeof costModelsActions.updateCostModel; - setDialogOpen: typeof costModelsActions.setCostModelDialog; -} - -interface PriceListTableState { - deleteRate: any; - index: number; - sortBy: { - index: number; - direction: SortByDirection; - }; - pagination: { - perPage: number; - page: number; - }; -} - -class PriceListTable extends React.Component { - protected defaultState: PriceListTableState = { - deleteRate: null, - index: -1, - sortBy: { - index: 0, - direction: SortByDirection.asc, - }, - pagination: { - perPage: 10, - page: 1, - }, - }; - public state: PriceListTableState = { ...this.defaultState }; - - public render() { - const { fetchStatus, fetchError, intl, isDialogOpen, isWritePermission, metricsHash } = this.props; - - const getMetricLabel = m => { - // Match message descriptor or default to API string - const value = m.replace(/ /g, '_').toLowerCase(); - const label = intl.formatMessage(messages.metricValues, { value }); - return label ? label : m; - }; - const getMeasurementLabel = m => { - // Match message descriptor or default to API string - const label = intl.formatMessage(messages.measurementValues, { - value: m.toLowerCase().replace('-', '_'), - count: 1, - }); - return label ? label : m; - }; - const metricOpts = Object.keys(metricsHash).map(m => ({ - label: getMetricLabel(m), // metric - value: m, - })); - const measurementOpts = metricOpts.reduce((acc, curr) => { - const measurs = Object.keys(metricsHash[curr.value]) - .filter(m => !acc.map(i => i.value).includes(m)) - .map(m => ({ label: getMeasurementLabel(m), value: m })); - return [...acc, ...measurs]; - }, []); - - const showAssignees = this.props.assignees && this.props.assignees.length > 0; - const cm = this.props.costModel; - const metric = this.state.deleteRate - ? `${this.state.deleteRate.metric.label_metric}-${this.state.deleteRate.metric.label_measurement} (${this.state.deleteRate.metric.label_measurement_unit})` - : ''; - return ( - <> - - - { - this.props.setDialogOpen({ name: 'deleteRate', isOpen: false }); - this.setState({ deleteRate: null }); - }} - isProcessing={this.props.isLoading} - onProceed={() => { - const { index } = this.state; - const { current } = this.props; - const newState = { - ...current, - source_uuids: current.sources.map(provider => provider.uuid), - source_type: 'OCP', - rates: [...current.rates.slice(0, index), ...current.rates.slice(index + 1)], - }; - this.props.updateCostModel(current.uuid, newState, 'deleteRate'); - }} - body={ - <> - {intl.formatMessage(messages.priceListDeleteRateDesc, { - metric: {metric}, - costModel: {cm}, - count: showAssignees ? 2 : 1, - })} - {showAssignees && ( - - {this.props.assignees.map(p => ( - {p} - ))} - - )} - - } - actionText={intl.formatMessage(messages.priceListDeleteRate)} - /> - - {({ search, setSearch, onRemove, onSelect, onClearAll }) => { - const getMetric = value => intl.formatMessage(messages.metricValues, { value }) || value; - const getMeasurement = (measurement, units) => { - units = intl.formatMessage(messages.units, { units: unitsLookupKey(units) }) || units; - return intl.formatMessage(messages.measurementValues, { - value: measurement.toLowerCase().replace('-', '_'), - units, - count: 2, - }); - }; - const from = (this.state.pagination.page - 1) * this.state.pagination.perPage; - const to = this.state.pagination.page * this.state.pagination.perPage; - - const res = this.props.current.rates - .map((r, i) => { - return { ...r, stateIndex: i }; - }) - .filter(rate => search.metrics.length === 0 || search.metrics.includes(rate.metric.label_metric)) - .filter( - rate => search.measurements.length === 0 || search.measurements.includes(rate.metric.label_measurement) - ) - .sort((r1, r2) => { - const projection = - this.state.sortBy.index === 1 - ? (r: Rate) => getMetric(r.metric.label_metric) - : this.state.sortBy.index === 2 - ? (r: Rate) => getMeasurement(r.metric.label_measurement, r.metric.label_measurement_unit) - : () => ''; - return compareBy(r1, r2, this.state.sortBy.direction, projection); - }); - const filtered = res.slice(from, to); - return ( - <> - setSearch({ primary })} - options={[ - { - label: intl.formatMessage(messages.metric), - value: 'metrics', - }, - { - label: intl.formatMessage(messages.measurement), - value: 'measurements', - }, - ]} - /> - } - selected={search.primary} - secondaries={[ - { - component: ( - onSelect('measurements', selection)} - options={measurementOpts} - /> - ), - name: 'measurements', - onRemove, - filters: search.measurements, - }, - { - component: ( - onSelect('metrics', selection)} - options={metricOpts} - /> - ), - name: 'metrics', - onRemove, - filters: search.metrics, - }, - ]} - button={ - - } - onClear={onClearAll} - pagination={ - - this.setState({ - pagination: { ...this.state.pagination, page }, - }) - } - onPerPageSelect={(_evt, perPage) => this.setState({ pagination: { page: 1, perPage } })} - titles={{ - paginationTitle: intl.formatMessage(messages.paginationTitle, { - title: intl.formatMessage(messages.priceList), - placement: 'top', - }), - }} - /> - } - /> - {fetchStatus !== FetchStatus.complete && } - {fetchStatus === FetchStatus.complete && fetchError && } - {fetchStatus === FetchStatus.complete && - filtered.length === 0 && - (search.metrics.length !== 0 || search.measurements.length !== 0) && } - {fetchStatus === FetchStatus.complete && - filtered.length === 0 && - search.measurements.length === 0 && - search.metrics.length === 0 && ( - - - - - {intl.formatMessage(messages.priceListEmptyRate)} - - {intl.formatMessage(messages.priceListEmptyRateDesc)} - - - )} - {fetchStatus === FetchStatus.complete && filtered.length > 0 && ( - <> - {intl.formatMessage(messages.readOnlyPermissions)} - ) : undefined, - onClick: (_evt, _rowIndex, rowData) => { - this.setState({ - deleteRate: null, - index: rowData.data.stateIndex, - }); - this.props.setDialogOpen({ - name: 'updateRate', - isOpen: true, - }); - }, - }, - { - title: intl.formatMessage(messages.delete), - isDisabled: !isWritePermission, - // HACK: to display tooltip on disable - style: !isWritePermission ? { pointerEvents: 'auto' } : {}, - tooltip: !isWritePermission ? ( -
{intl.formatMessage(messages.readOnlyPermissions)}
- ) : undefined, - onClick: (_evt, _rowIndex, rowData) => { - const rowIndex = rowData.data.stateIndex; - this.setState({ - deleteRate: filtered[rowIndex], - index: rowIndex + from, - }); - this.props.setDialogOpen({ - name: 'deleteRate', - isOpen: true, - }); - }, - }, - ]} - tiers={filtered} - sortCallback={e => { - this.setState({ - ...this.state, - sortBy: { ...e }, - }); - }} - /> - - - - - - this.setState({ - pagination: { ...this.state.pagination, page }, - }) - } - onPerPageSelect={(_evt, perPage) => - this.setState({ - pagination: { page: 1, perPage }, - }) - } - titles={{ - paginationTitle: intl.formatMessage(messages.paginationTitle, { - title: intl.formatMessage(messages.priceList), - placement: 'bottom', - }), - }} - variant="bottom" - /> - - - - - )} - - ); - }} -
- - ); - } -} - -export default injectIntl( - connect( - createMapStateToProps(state => ({ - isLoading: costModelsSelectors.updateProcessing(state), - error: costModelsSelectors.updateError(state), - isDialogOpen: costModelsSelectors.isDialogOpen(state)('rate'), - fetchError: costModelsSelectors.error(state), - fetchStatus: costModelsSelectors.status(state), - metricsHash: metricsSelectors.metrics(state), - costTypes: metricsSelectors.costTypes(state), - isWritePermission: rbacSelectors.isCostModelWritePermission(state), - })), - { - updateCostModel: costModelsActions.updateCostModel, - setDialogOpen: costModelsActions.setCostModelDialog, - } - )(PriceListTable) -); diff --git a/src/routes/costModels/costModel/sourceTable.tsx b/src/routes/costModels/costModel/sourceTable.tsx deleted file mode 100644 index f6d5b7531..000000000 --- a/src/routes/costModels/costModel/sourceTable.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import type { CostModel, CostModelProvider, CostModelRequest } from 'api/costModels'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; -import { createMapStateToProps } from 'store/common'; -import { costModelsActions, costModelsSelectors } from 'store/costModels'; - -import AddSourceWizard from './addSourceWizard'; -import Dialog from './dialog'; -import Table from './table'; - -interface SourceTableProps extends WrappedComponentProps { - updateCostModel: typeof costModelsActions.updateCostModel; - costModel: CostModel; - sources: CostModelProvider[]; - isLoading: boolean; - setDialogOpen: typeof costModelsActions.setCostModelDialog; - isDialogOpen: { deleteSource: boolean; addSource: boolean }; -} - -interface SourceTableState { - dialogSource: string; -} - -class SourceTableBase extends React.Component { - protected defaultState: SourceTableState = { - dialogSource: null, - }; - public state: SourceTableState = { ...this.defaultState }; - - public render() { - const { intl, isDialogOpen, isLoading, setDialogOpen, sources, costModel } = this.props; - - return ( - <> - {isDialogOpen.addSource && ( - setDialogOpen({ name: 'addSource', isOpen: false })} - onSave={(source_uuids: string[]) => { - this.props.updateCostModel( - costModel.uuid, - { - ...costModel, - source_type: costModel.source_type === 'OpenShift Container Platform' ? 'OCP' : 'AWS', - source_uuids, - } as CostModelRequest, - 'addSource' - ); - }} - /> - )} - { - setDialogOpen({ name: 'deleteSource', isOpen: false }); - this.setState({ dialogSource: null }); - }} - isProcessing={isLoading} - onProceed={() => { - const newState = { - ...costModel, - source_type: costModel.source_type === 'OpenShift Container Platform' ? 'OCP' : 'AWS', - source_uuids: sources - .filter(provider => provider.name !== this.state.dialogSource) - .map(provider => provider.uuid), - }; - this.props.updateCostModel(costModel.uuid, newState, 'deleteSource'); - }} - body={ - <> - {intl.formatMessage(messages.costModelsSourceDeleteSourceDesc, { - source: this.state.dialogSource, - costModel: costModel.name, - })} - - } - actionText={intl.formatMessage(messages.costModelsSourceDeleteSource)} - /> - { - this.setState({ dialogSource: item[0] }); - setDialogOpen({ name: 'deleteSource', isOpen: true }); - }} - onAdd={() => setDialogOpen({ name: 'addSource', isOpen: true })} - rows={sources.map(p => p.name)} - current={costModel} - /> - - ); - } -} - -export default injectIntl( - connect( - createMapStateToProps(state => ({ - isLoading: costModelsSelectors.updateProcessing(state), - isDialogOpen: costModelsSelectors.isDialogOpen(state)('sources'), - })), - { - setDialogOpen: costModelsActions.setCostModelDialog, - updateCostModel: costModelsActions.updateCostModel, - } - )(SourceTableBase) -); diff --git a/src/routes/costModels/costModel/sourcesTable.tsx b/src/routes/costModels/costModel/sourcesTable.tsx deleted file mode 100644 index e8563ccb5..000000000 --- a/src/routes/costModels/costModel/sourcesTable.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import type { IAction, IRow } from '@patternfly/react-table'; -import { ActionsColumn, TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; -import { TableGridBreakpoint } from '@patternfly/react-table'; -import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; -import { createMapStateToProps } from 'store/common'; -import { costModelsSelectors } from 'store/costModels'; -import { rbacSelectors } from 'store/rbac'; - -interface SourcesTableOwnProps { - showDeleteDialog: (rowId: number) => void; -} - -interface SourcesTableStateProps { - canWrite: boolean; - costModels: any[]; -} - -type SourcesTableProps = SourcesTableOwnProps & SourcesTableStateProps & WrappedComponentProps; - -const SourcesTable: React.FC = ({ canWrite, costModels, intl, showDeleteDialog }) => { - const getActions = (): IAction[] => { - if (canWrite) { - return [ - { - title: intl.formatMessage(messages.costModelsSourceDelete), - onClick: (_evt, rowIndex: number) => showDeleteDialog(rowIndex), - }, - ]; - } - return [ - { - style: { pointerEvents: 'auto' }, - tooltip: intl.formatMessage(messages.readOnlyPermissions), - isDisabled: true, - title: intl.formatMessage(messages.costModelsSourceDelete), - }, - ]; - }; - - const actions = getActions(); - const rows: (IRow | string[])[] = costModels.length > 0 ? costModels[0].sources.map(source => [source.name]) : []; - - return ( - - - - - - - - - {rows.map((r: any, rowIndex) => ( - - - - - ))} - - - ); -}; - -export default injectIntl( - connect( - createMapStateToProps(state => { - return { - canWrite: rbacSelectors.isCostModelWritePermission(state), - costModels: costModelsSelectors.costModels(state), - }; - }) - )(SourcesTable) -); diff --git a/src/routes/costModels/costModel/sourcesToolbar.tsx b/src/routes/costModels/costModel/sourcesToolbar.tsx deleted file mode 100644 index 44076f5f3..000000000 --- a/src/routes/costModels/costModel/sourcesToolbar.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import type { ButtonProps, PaginationProps } from '@patternfly/react-core'; -import { - Button, - InputGroup, - InputGroupText, - Pagination, - TextInput, - Toolbar, - ToolbarContent, - ToolbarFilter, - ToolbarItem, - ToolbarToggleGroup, -} from '@patternfly/react-core'; -import { FilterIcon } from '@patternfly/react-icons/dist/esm/icons/filter-icon'; -import { SearchIcon } from '@patternfly/react-icons/dist/esm/icons/search-icon'; -import React from 'react'; -import { ReadOnlyTooltip } from 'routes/costModels/components/readOnlyTooltip'; - -interface FilterInputProps { - id: string; - value: string; - onChange: (value: string, event: React.FormEvent) => void; - onSearch: (evt: React.KeyboardEvent) => void; - placeholder?: string; -} - -const FilterInput: React.FC = ({ id, placeholder = '', value, onChange, onSearch }) => { - return ( - - ) => { - if (evt.key !== 'Enter' || value === '') { - return; - } - onSearch(evt); - }} - /> - - - - - ); -}; - -interface SourcesToolbarProps { - actionButtonProps: ButtonProps; - paginationProps?: PaginationProps; - filterInputProps: FilterInputProps; - filter: { - onRemove: (category: string, chip: string) => void; - onClearAll: () => void; - query: { name?: string[] }; - categoryNames: { name?: string }; - }; -} - -export const SourcesToolbar: React.FC = ({ - filterInputProps, - paginationProps, - filter, - actionButtonProps, -}) => { - return ( - - - }> - - - - - - - - - , - , - ]} - > - <> - {updateError && } -
- - this.setState({ name: value })} - /> - - -
{intl.formatMessage(messages.names, { count: 1 })}
{r} - { - return { - ...a, - onClick: () => a.onClick(null, rowIndex, r, null), - }; - })} - > -